考虑下面的C++程序
template<class T>
struct C;
template<class T>
struct B{
void f(){
cout << "a=" << C<T>::a << endl;
}
};
template<class T>
struct C{static const int a = 1;};
int main()
{
B<int> b;
b.f();
return 0;
}
g和clang认为它是一个正确的C++程序,好的,C<T>::a
应该是一个依赖名,编译器会在示例化中查找它(即,当b.f()
被调用时)。
令我困惑的是,cppreference说ADL用于函数调用表达式,那么如果这里ADL没有启动,编译器如何找到a
的声明呢?
我试过下面的程序,它显示在定义中的普通查找找不到a
的声明?
#include <iostream>
using namespace std;
class D;
struct E{
void f(){
cout << D::a << endl; // error: incomplete type ‘D’ used in nested name specifier
}
};
class D{
static const int a = 7;
};
int main()
{
E e;
e.f();
return 0;
}
1条答案
按热度按时间wfsdck301#
首先,我阅读了伊戈尔·坦德尼克引用的《标准》中的相关段落,我觉得不能用它来解释我的问题。
[临时部门类型]/8表示
对于给定的模板参数集,如果引用具有限定id或类成员访问表达式的当前示例化的成员的模板的专门化被示例化,则在模板示例化上下文中查找限定id或类成员访问表达式中的名称。如果该查找的结果与在模板定义上下文中的名称查找的结果不同,名称查找不明确。
标准也给出了一个例子
这里
f()
和g()
中的m
和this->m
指的是当前示例化C<T>::A::m
的成员(因为A
是一个非依赖基类)。所以根据这个规则,在template示例化上下文中再次查找
this->m
,找到B::m
,错误出现,但是g()
中的m
不是qualified-id或者类成员访问表达式,所以template int C<B>::g();
是安全的。回到让我困惑的例子,
C<T>::a
实际上是一个未知特化的成员.[temp.dep.type]/6:如果名称是限定id,其中嵌套名称说明符命名不是当前示例化的依赖类型,则该名称是未知专门化的成员。
尴尬的是我在标准中只找到了函数名的查找规则,我想编译器只是做一个普通的查找依赖名,它不是模板示例化上下文中的函数名。
关于示例化点,[温度点]/4
对于类模板专门化、类成员模板专门化、或类模板的类成员的专门化,如果专门化由于从另一模板专门化内引用而被隐式地示例化,如果从其引用专门化的上下文依赖于模板参数,以及如果专门化在封闭模板的示例化之前没有被示例化,示例化的点在封闭模板的示例化的点之前。否则,这种专门化的示例化的点在引用该专门化的命名空间作用域声明或定义之前。
因此,
template struct C
的示例化点在可以访问template struct B
和C<T>::a
之前。但是NathanOliver的直观解释“当
B<T>::f()
被示例化时,名称a
得到解析”对于日常编程来说可能已经足够了。