依赖名称的模板消歧器

The template disambiguator for dependent names

本文关键字:依赖      更新时间:2023-10-16

此问题基于C++引用部分:依赖名称-依赖名称的模板消歧器。

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

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
}

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

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

#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函数模板

似乎没有任何情况下使用非成员模板函数可以很好地作为类的id表达式成员访问表达式。

我认为这说明了这个查找规则的意图:它应该找到格式良好的结构;它并不是为了让解析器对那些具有一些临时匹配的<>感到满意,这些临时匹配稍后将被具有完全不同语义的其他内容所取代。

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

当成员模板专用化的名称出现在后面时。或->在后缀表达式中,或在限定id中的嵌套名称说明符之后,并且后缀表达式的对象表达式为限定id中的类型相关或嵌套的名称说明符引用到依赖类型,但该名称不是当前类型的成员实例化(14.6.2.1),成员模板名称必须以为前缀关键字CCD_ 10。否则,该名称将被假定为非模板。

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

或者,换言之,如果你想将set解析为成员模板的名称,它前面必须有template。如果没有,它可以解析为命名空间范围set,但set名称本身不再是依赖于模板参数的名称(set<T>仍然是依赖的,但set本身不是)。然后我们进入[14.6p10],上面写着:

如果名称不依赖于模板参数(如中所定义14.6.2),该名称的声明(或声明集)应在该名称出现在模板中时的范围内释义名称绑定到声明并且此绑定不受声明的影响在实例化时可见。

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

此外,关于[3.4.5p1]中查找规则对此类示例的适用性,还有一个悬而未决的问题——问题1835。引用其中的注释:

一种可能是将查找限制为对象表达式时的对象表达式。

该问题处于起草阶段,这意味着工作组已达成非正式共识。这一共识究竟是什么还有待观察,但我想说,很有可能会有所改变。依赖这样的代码似乎不是一个好主意。


从C++11到当前工作草案(N4431),引用的段落保持不变。