简体   繁体   中英

The template disambiguator for dependent names

This issue is based on section C++ reference : dependent name - The template disambiguator for dependent name .

I have understood when invoking the template member function in a template class, the keyword template is necessary to make the compiler know the following bracket is used for indicating template argument.just like the used example in this section.

template<typename T>
struct S {
    template<typename U> void foo(){}
};

template<typename T>
void bar()
{
    S<T> s;
    s.foo<T>(); // error: < parsed as less than operator
    s.template foo<T>(); // OK
}

However, in the consequent part it describes when a template name appears in a member access expression (after -> or after .), the disambiguator is unnecessary if there is a template with the same name found by ordinary lookup in the context of the expression. .

Then it comes with the following code. Comparing with previous example it defines set function whose name exists in standard library as well. At the meanwhile, using std::set is set up to make set template visible in template function. In this condition, even if keyword template is not provided it still works well.

#include <set>
using std::set; // makes 'set' visible to lookup from bar

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

template<typename T>
void bar()
{
    S<T> s;
    s.set<T>(); // not an error if ::set is visible:
                // (and since C++11, this is well-formed)
    s.template set<T>(); // works with and without ::set
}

Based on my understanding, I tried my own version

#include <iostream>

template <typename T> 
struct S{
    template <typename U> void func(){
        std::cout << "In S::func\n";
    }
};

// In order to make member template function is visible in function test,
// defining a global template function **func** whose name is same with one 
// member template function in struct S.
template <typename M>
void func(){
    std::cout << "from ordinary func\n";
}

template <typename M>
void test(){
    S<M> s;
    func<M>();     // test func template function is visible in test function
    s.func<M>();     
}
int main(){    
    test<int>();
}

The detail error message is listed as follows

[17:17:50][ryu@C++_test]$ g++ -g typename2.cpp 
typename2.cpp:61:7: error: use 'template' keyword to treat 'func' as a dependent
      template name
    s.func<M>();
      ^
      template 
1 error generated.

Any advice is appreciated on how to make my own code works well without keyword template.

Short version

Don't rely on this. Use the template keyword as you're supposed to, don't try such an obscure hack only to avoid a few keystrokes. Your code should definitely not compile according to the standard, and the example you quoted from cppreference.com may soon become explicitly disallowed as well (I don't think it was valid in the first place). GCC and Clang yield different results for both these examples. Even if they compile today, they may fail tomorrow in the next compiler version.

Long version

Regarding the example from cppreference.com , let's first note that Clang 3.6.0 compiles the code, but GCC 5.1.0 rejects both s.set<T>() and s.template set<T>() with the error invalid use of 'class std::set<T>' . I don't think either of the two compilers does the right thing here according to the standard, but, intuitively, GCC's error message makes a lot of sense: what would be the meaning of s.set<T>() with set<T> being a class template specialization?

With your code, it's the other way around: Clang rejects it (the error message quoted in the question seems to be actually from Clang) and GCC compiles it.

The examples rely on paragraph [3.4.5p1] in the standard (emphasis mine in all quotes):

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.

The class template part is the reason for your code being rejected by Clang (your func is a function template ). Function templates were removed from there as the resolution of defect 141 , included in C++11. It's worth mentioning a comment in the defect report:

There do not seem to be any circumstances in which use of a non-member template function would be well-formed as the id-expression of a class member access expression.

I think this says something about the intent of this lookup rule: it's supposed to find constructs that are well formed ; it's not intended to just make the parser happy about those <> with some temporary match that will later be replaced by something else with entirely different semantics.

Even with the special lookup rule above, I'm not sure the standard allows you to omit the template in such cases. Paragraph [14.2p4] says:

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 object expression of the postfix-expression is type-dependent or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not 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.

Both member templates set and func in the two examples satisfy the conditions in there, respectively. As you can see, there's no mention of an exception to this rule.

Or, to put it another way, if you want set to be resolved as the name for the member template, it has to have template in front of it. If it doesn't, it can be resolved as the namespace-scope set , but then the set name itself is no longer a name dependent on template parameters ( set<T> is still dependent, but set itself is not). And then we get to [14.6p10], which says:

If a name does not depend on a template-parameter (as defined in 14.6.2), a declaration (or set of declarations) for that name shall be in scope at the point where the name appears in the template definition; the name is bound to the declaration (or declarations) found at that point and this binding is not affected by declarations that are visible at the point of instantiation.

Once bound, it's carved in stone and the later switch to the member template is not valid, which makes the cppreference.com example incorrect.

Moreover, there's an open issue regarding the applicability of the lookup rule from [3.4.5p1] to such examples - issue 1835 . Quoting a note in there:

One possibility might be to limit the lookup to the class of the object expression when the object expression is dependent.

The issue is in drafting status, which means informal consensus has been reached in the working group . Exactly what that consensus was remains to be seen, but I'd say there's a good chance that something will change. Relying on such code doesn't seem like a good idea.


The quoted paragraphs have remained unchanged since C++11 up to the current working draft (N4431).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM