Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invalid template dependent member function template deduction - thinks I'm trying to use std::set

Tags:

c++

templates

stl

I have a class template which inherits from a base class template.

The base class template has a data member with a member function template which I want to call from within my super class.

I know that in order to disambiguate the call to the member function template, I have to use the template keyword, and I have to explicitly refer to this in the super class.

this->base_member_obj.template member_function<int>();

All this is good and well, except that the codebase I'm using has made the rather unfortunate mistake of importing the entirety of namespace std, and the template member function I'm trying to call is called set. Somewhere in the framework std::set is included, and this causes GCC to think I'm trying to declare a std::set rather than call the member function set.

GCC 4.7 throws an error invalid use of 'class std::set'

See below for an example showing the error. If you comment out using namespace std the code compiles fine.

Sadly it is not feasible for me to go through the entire codebase, remove every using namespace std call, and prefix every call to anything inside the std namespace with std::

Is there any other way around this?

#include <set>
using namespace std; // comment this out to compile fine

struct blah
{
    template<typename T>
    void set()
    { }
};

template<typename T>
struct base
{
    blah b;
};

template<typename T>
struct super : base<super<T>>
{
    void fun()
    {
        this->b.template set<int>(); // this line breaks
    }
};

int main()
{
    super<int> s;
    s.fun();

    return 0;
}
like image 678
Steve Lorimer Avatar asked May 03 '12 06:05

Steve Lorimer


3 Answers

Well, that's rather embarrassing for us C++ pushers.

This is a bug in G++, which also appears in Comeau Test Drive. It's not a flaw in the language itself. The problem stems from the left-to-right nature of parsing and way C++ grammar skirts ambiguity.

It is legal to use a non-member, non-base class template in a nested name specifier to a typedef to a base class template. In such a context, a class template with no special relationship to the accessed class can appear after the ->:

#include <tuple>

template< typename t >
struct get_holder
    { typedef std::tuple< t > type; };

template< typename ... ts >
struct inherits
    : get_holder< ts >::type ... {

    inherits( ts ... v )
        : get_holder< ts >::type( v ) ...
        {}

    template< typename tn >
    void assign_one( inherits &o )
        { this->get_holder< tn >::type::operator= ( o ); } // <- here!
};

int main() {
    inherits< int, char, long > icl( 3, 'q', 2e8 );
    icl.assign_one< char >( icl );
}

Since C++ is parsed left-to-right, when the parser hits the ->, it must resolve get_holder before proceeding. The Standard has a special clause §3.4.5/1 [basic.lookup.classref] for this:

In a class member access expression (5.2.5), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (14.2) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class template. If the lookup in the class of the object expression finds a template, the name is also looked up in the context of the entire postfix-expression and

— if the name is not found, the name found in the class of the object expression is used, otherwise

— if the name is found in the context of the entire postfix-expression and does not name a class template, the name found in the class of the object expression is used, otherwise

if the name found is a class template, it shall refer to the same entity as the one found in the class of the object expression, otherwise the program is ill-formed.

Emphasis mine — it would appear that G++ is following this logic despite the template keyword appearing between the . and the identifier set. Furthermore, assuming it's going down this route, it should have flagged the ambiguity as an error instead of trying to choose the non-member.

There does appear to be a flaw in the standard's wording of how to proceed when the template keyword does appear, but it shouldn't cause the mayhem you see. §14.2 [temp.names]:

When the name of a member template specialization appears after . or -> in a postfix-expression, or after a nested-name-specifier in a qualified-id, and the postfix-expression or qualified-id explicitly depends on a template-parameter (14.6.2) but does not refer to a member of the current instantiation (14.6.2.1), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.

Emphasis mine, that text is in error and should read "the name is assumed not to name a member template," because as in my illustration above it could be part of a nested name specifier. If the text is taken literally as-is, then it could be interpreted to mean the template keyword is required in my illustration, hence could indicate a following non-member template (assuming the language supports such a construct at all), and then your program could be misinterpreted as seen by G++.

But the design intent is clear, and you do not need to add an artificial nested name specifier blah::, although it is a legal workaround.

like image 43
Potatoswatter Avatar answered Nov 16 '22 02:11

Potatoswatter


Try this:

this->b.blah::template set<int>(); // this line breaks
like image 137
inkooboo Avatar answered Nov 16 '22 02:11

inkooboo


You have two options:

    this->b.blah::template set<int>(); 
     //     ^^^^^^ Be very explicit what set you want

Alternatively don't use using namespace (the best option).

like image 3
Martin York Avatar answered Nov 16 '22 01:11

Martin York