从属名称的模板消歧器
此问题基于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
为前缀。 否则,该名称被假定为命名一个非模板。
这两个示例中的成员模板set
和func
分别满足那里的条件。 正如你所看到的,没有提到这个规则的例外。
或者,换句话说,如果您希望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