从属名称的模板消歧器

此问题基于C ++部分参考:依赖名称 - 从属名称的模板消除歧义。

我已经理解了在模板类中调用模板成员函数时,为了使编译器知道以下括号用于指示模板参数,必须使用关键字模板。就像本节中使用的示例一样。

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
}

但是,在随后的部分中,它描述了模板名称何时出现在成员访问表达式(后 - >或之后)中,如果在表达式的上下文中通过普通查找找到具有相同名称的模板,则不需要消歧器。

然后它带有下面的代码。 与前面的例子相比,它定义了名称存在于标准库中的set函数。 同时, 使用std :: set来设置模板函数中可见的设置模板。 在这种情况下,即使没有提供关键字模板,它仍然运作良好。

#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
}

根据我的理解,我尝试了我自己的版本

#include <iostream>

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

// 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 funcn";
}

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>();
}

详细错误消息列出如下

[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.

任何意见,如何使我自己的代码没有关键字模板运行良好。


简洁版本

不要依赖这个。 按照你应该使用的template关键字,不要尝试这样一个晦涩的黑客只是为了避免一些击键。 你的代码绝对不应该按照标准进行编译,你从cppreference.com引用的例子可能很快就会被明确地禁止(我认为它首先不是有效的)。 对于这两个例子,GCC和Clang都会得出不同的结果。 即使他们今天编译,他们明天也可能在下一个编译器版本中失败。

长版

关于cppreference.com的例子,我们首先注意到Clang 3.6.0编译代码,但GCC 5.1.0拒绝s.set<T>()s.template set<T>() ,并且错误invalid use of 'class std::set<T>' 。 根据标准,我认为这两个编译器都没有做正确的事情,但直观地看,GCC的错误信息有很大的意义: s.set<T>()set<T>是一个类模板专业化?

与你的代码相反,Clang拒绝它(在问题中引用的错误消息似乎实际上来自Clang),GCC编译它。

这些例子依赖于标准中的段落[3.4.5p1](在所有引用中强调我的意思):

在类成员访问表达式(5.2.5)中,如果。 或 - >标记之后紧跟着一个标识符,后跟一个<,则必须查找标识符以确定<是模板参数列表(14.2)的开始还是小于运算符。 标识符首先在对象表达式的类中查找。 如果找不到标识符,则在整个后缀表达式的上下文中查找它,并命名一个模板。

类模板部分是您的代码被Clang拒绝的原因(您的func是一个函数模板)。 函数模板作为C ++ 11中包含的缺陷141的解决方案从中删除。 值得一提的是缺陷报告中的评论:

似乎没有任何情况下,非成员模板函数的使用将与类成员访问表达式的id表达式一样良好。

我认为这说明了这个查找规则的意图:它应该找到结构良好的构造; 它并不打算仅仅通过一些临时匹配让分析器对这些<>高兴,这些匹配稍后将被具有完全不同语义的其他事物替代。

即使使用上面的特殊查找规则,我也不确定标准是否允许您在这种情况下省略template 。 第[14.2p4]段说:

当成员模板专业化的名称出现后。 或 - 在后缀表达式中或在限定id中的嵌套名称说明符之后,并且postfix-expression的对象表达式是依赖于类型的,或者qualified-id中的嵌套名称说明符是指但名称不是当前实例的成员(14.6.2.1),成员模板名称必须以关键字template为前缀。 否则,该名称被假定为命名一个非模板。

这两个示例中的成员模板setfunc分别满足那里的条件。 正如你所看到的,没有提到这个规则的例外。

或者,换句话说,如果您希望set为成员模板的名称,则必须在其前面有template 。 如果不是这样,就可以解决作为命名空间范围set ,但随后的set名称本身不再依赖于模板的参数的名称( set<T>仍然是相关的,但是set本身是不是)。 然后我们得到[14.6p10],其中说:

如果名称不依赖于模板参数(如14.6.2所定义),那么该名称的声明(或声明集)应该在模板定义中名称出现的位置范围内; 该名称被绑定到在该点发现的声明(或声明),并且该绑定不受在实例化处可见的声明的影响。

一旦绑定,它就会被刻在石头上,之后切换到成员模板是无效的,这使得cppreference.com示例不正确。

此外,关于[3.4.5p1]查找规则对这些示例(问题1835)的适用性存在一个悬而未决的问题。在那里引用一个注释:

当对象表达式依赖时,一种可能性可能是将查找限制为对象表达式的类。

问题在于起草状态,这意味着工作组已达成非正式共识。 究竟是什么共识还有待观察,但我会说有一个很好的机会会改变一些事情。 依靠这样的代码似乎不是一个好主意。


从C ++ 11到现在的工作草案(N4431)之后,引用的段落保持不变。

链接地址: http://www.djcxy.com/p/26035.html

上一篇: The template disambiguator for dependent names

下一篇: Drawing a tree using recursion