在C ++模板实例化中查找从属名称

埃尔玛·赞德(Elmar Zander)

当我尝试编译这段代码时

// void foobar(int); 

template <class T>
struct Foo {
  void bar(T t) { foobar(t); };
};

void foobar(int);

template class Foo<int>;

与g ++ 4.8.2我收到以下错误消息

foo.cc: In instantiation of ‘void Foo<T>::bar(T) [with T = int]’:
foo.cc:10:16:   required from here
foo.cc:5:27: error: ‘foobar’ was not declared in this scope, and no 
             declarations were found by argument-dependent lookup at 
               the point of instantiation [-fpermissive]
   void bar(T t) { foobar(t); };
                           ^
foo.cc:8:6: note: ‘void foobar(int)’ declared here, later in the translation unit
 void foobar(int);
      ^

(使用clang 3.4几乎相同)。

首先,我认为代码是正确的并且应该编译,因为foobar是模板声明中的从属名称,并且仅在实例化模板的第二阶段才应进行查找。在最后一行完成此操作时,已经声明了“ foobar(int)”。当我取消注释最顶行时,代码会编译,顺便说一句,但是两个声明都在实例化之前,因此这无关紧要。

其次,错误消息本身似乎与我矛盾。它说“在声明点找不到声明”,即foo.cc:10:16,并说它在foo.cc:8:6声明为“以后”。就我所知道的有关数字和英语的所有知识而言,我会称其为“之前”而不是“以后”。

那么,这是gcc中的错误还是我做错了?但是,由于在我看来这是一种常见的使用模式,因此我无法相信。

顺便说一句:当我尝试使用g ++在MSDN(http://msdn.microsoft.com/en-us/library/dx2zs2ee.aspx)上使用M ++的第二个示例“依赖类型的名称解析”时,结果与vc ++有所不同(不是一般情况,但在这种情况下)会破坏这是g ++中的错误。

MM

tl; dr Foo<int>不会调用任何ADL,但Foo<X>调用(其中X是类类型)。


首先,foobar由于(C ++ 14 / N3936),此代码中的从属名称[temp.dep]/1

在以下形式的表达式中:

postfix-expression ( expression-list opt )

如果[...]后缀表达式是一个不合格ID,则不合格ID表示从属名称。

  • expression-list中的任何表达式都是类型相关的表达式(14.6.2.2),或者

并且t是从属名称,因为它是声明的一部分,T t其中T是模板参数,因此是从属类型。

转向依赖名称解析,[temp.dep.res]/1这引入了一个事实,即可以在定义上下文和实例化上下文中查找名称,并定义实例化上下文的位置。为了简洁起见,我省略了它,但是在此示例中template class Foo<int>;是实例化的要点。

下一位是[temp.dep.candidate]/1

对于后缀表达式为从属名称的函数调用,将使用常规查找规则(3.4.1、3.4.2)查找候选函数,但以下情况除外:

  • 对于使用非限定名称查找(3.4.1)的查找部分,仅从模板定义上下文中找到函数声明。
  • 对于使用关联的名称空间(3.4.2)进行的查找,仅会找到在模板定义上下文或模板实例化上下文中找到的函数声明。

最后两个部分是两阶段查找的“两个阶段”。(注意-本节的措词从C ++ 11更改为C ++ 14,但效果相同)。

在第一个阶段3.4.1中,找不到的名称foobar


因此,我们进入第二阶段。按照3.4.2中的描述查找名称的实际位置。文本很长,但是这里有两个相关规则:

  • 如果T是基本类型,则其关联的名称空间和类的集合均为空。

  • 如果T是一个类类型(包括并集),则其关联的类为:类本身;它所属的类(如果有);及其直接和间接基类。其关联的名称空间是其关联类的最内层封闭的名称空间。[...]

因此,当实例化时Foo<int>,查找的第二阶段不会引入任何其他要搜索的名称空间。

但是,如果将示例更改为拥有struct X {};,然后更改intX无处不在,则代码编译。这是因为后一个要点:类类型的参数的ADL会搜索该类的封闭名称空间(现在是全局名称空间),而内置类型的参数的ADL不会搜索全局名称空间。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

C ++模板实例化:避免长时间切换

C ++ 14中的从属限定名称查找

为什么C ++函数模板实例化的返回类型包含在错误的函数名称中?

C ++解析器如何区分比较和模板实例化?

从模板实例化后声明的模板函数选择候选中调用的C ++模板过载解析

GNU C ++和Clang中的模板实例化

在C ++模板实例化期间获取原始结构/类名称

如何在C ++中编写代码以执行条件模板实例化

C ++ 17单独的显式方法模板实例化声明和定义

基类模板中的从属名称查找

具有if语句的C ++模板实例化

在可以区分比较和模板实例化之前,C ++解析器会做什么?

C ++概念:检查模板实例化

GCC中的模板实例化与Visual C ++不同

使用clang的显式C ++模板实例化

在C ++中的模板实例化中将带有构造函数的类用作类型参数

在这种情况下,c ++标准是否保证模板实例化?

相互引用的C ++模板实例化

模板实例变量的C ++检查类型

C ++中重载乘法运算符的递归类模板实例化期间的错误

努力理解C ++模板实例化

为什么C ++模板实例化失败?

c ++概念是否导致模板实例化被编写以生成输出?

跨DLL的C ++成员函数显式模板实例化

C ++ VS2008-类模板实例化上的奇怪错误

具有复杂类型的C ++模板实例化

可以实例化c ++类模板,但是具有相同模板参数的功能模板实例化失败

C ++,使用模板实例化存储对象实例的通用节点

C++ 中的从属名称是什么?