在模板实例化中查找从属名称C++
Lookup of dependent names in C++ template instantiation
当我尝试编译此代码时
// 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)"。顺便说一句,当我取消注释最上面的行时,代码会编译,但两个声明都在实例化之前,所以这无关紧要。
其次,错误消息本身对我来说似乎是矛盾的。它说"在instiiation点没有找到声明",这是foo.cc:10:16,它说它在foo.cc:8:6上声明"稍后"。对于我对数字和英语的了解,我会称之为"之前"而不是"以后"。
那么,这是 gcc 中的错误还是我弄错了什么?然而,由于在我看来这是一种常见的使用模式,我不太相信。
顺便说一句:当我使用 g++ 在 MSDN (http://msdn.microsoft.com/en-us/library/dx2zs2ee.aspx) 上尝试第二个"依赖类型的名称解析"示例时,结果与 vc++ 不同,后者(不是一般的,但在这种特定情况下)会破坏这是 g++ 中的一个错误。
tl;dr Foo<int>
不会调用任何 ADL,但Foo<X>
会(其中 X
是一种类类型)。
首先,在此代码中,由于 (C++14/N3936) [temp.dep]/1
,foobar
是一个依赖名称
在以下形式的表达式中:
postfix-expression ( expression-list opt )
其中后缀表达式是非限定 id,非限定 id 表示从属名称,如果 [...]
表达式
- 列表中的任何表达式都是与类型相关的表达式 (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 {};
,然后将int
更改为 X
所有位置,则代码确实可以编译。这是因为后一个要点:类类型参数的 ADL 会搜索该类的封闭命名空间(现在是全局命名空间),但是内置类型参数的 ADL 不会搜索全局命名空间。
第二阶段查找仅包括无法在第一阶段(例如 ADL)中应用的名称查找规则。两阶段查找正是这样 - 一些名称在第一阶段查找,一些名称在第二阶段查找。这种特殊类型的名称是第一阶段的名称,因为编译器完全能够在第一阶段在函数的命名空间中查找foobar
。
可视C++不实现两阶段名称查找。
看起来是正确的。虽然重载解析仅在阶段 2 中完成,但在阶段 1 中,您已经必须知道 foobar(t)
是一个函数调用表达式。如果foobar
命名一个类型,t
甚至不会是一个依赖名称。
- 正在查找文档以获得PS4平台的C++中的设备信息
- 在C++中查找文件
- 模板元程序查找相似的连续类型名称
- 在UNIX系统中使用DIR查找文件的字节大小
- 查找最接近的大于当前数字的数字的索引
- 有没有一种方法可以创建一个带有哈希表的数据库,该哈希表具有恒定时间查找功能
- 查找后更改类变量
- 使用正则表达式regex_search在字符串中查找字符串
- 使用gcc从静态链接的文件中查找可选符号
- 在C++中查找范围的长度
- 算法问题:查找从堆栈中弹出的所有序列
- 在Windows中查找扬声器输出的当前音量级别
- 如何在C++中使用X509证书模在令牌中查找私钥
- 使用.find函数在c++中查找字符和另一个字符之间的大小
- 在 Windows 上,是否可以让 dll 在不使用 PATH 环境变量的情况下在另一个文件夹中查找依赖项?
- 在 for 循环中查找问题时遇到困难
- 如何在文件中查找字节序列
- 试图创建一个多线程程序来查找0-100000000之间的总素数
- 使用堆查找第K个最大元素的时间复杂性
- 一个函数,用于查找字符串1包含字符串2 c++的次数