编译并运行代码时
#include <iostream>
struct A { A(int){} };
void foo( int ) { std::cout << "foo(int)" << std::endl; }
template< typename T >
struct S {
void bar() { foo( 5 ); }
void foobar() { T t = 5; foo(t); }
};
inline void foo( A ) { std::cout << "foo(A)" << std::endl; }
inline void foo( double ) { std::cout << "foo(double)" << std::endl; }
int main(int argc, char* argv[])
{
S<double> s0;
s0.bar();
s0.foobar();
std::cout << '\n';
S<A> s1;
s1.bar();
s1.foobar();
}
我得到输出(使用g ++ 4.8,clang ++ 3.2或icpc 13.1)
foo(int)
foo(int)
foo(int)
foo(A)
虽然考虑到两阶段查找规则,最后两行对我来说非常有意义,但我希望前两行有foo(int) foo(double)
。
在这种情况下,foobar()
调用foo()
似乎在实例化之前被查找,这是不可能的。任何提示?
答案 0 :(得分:5)
在S:
之前定义foo(double)#include <iostream>
struct A { A(int){} };
void foo( int ) { std::cout << "foo(int)" << std::endl; }
inline void foo( double ) { std::cout << "foo(double)" << std::endl; } //here
inline void foo( A ) { std::cout << "foo(A)" << std::endl; }
template< typename T >
struct S {
void bar() { foo( 5 ); }
void foobar() { T t = 5; foo(t); }
};
int main(int argc, char* argv[])
{
S<double> s0;
s0.bar();
s0.foobar();
std::cout << '\n';
S<A> s1;
s1.bar();
s1.foobar();
}
输出:
foo(int)foo(double)
foo(int)foo(A)
编辑: 所以S :: foo是一个非依赖名称,在定义模板时会被解析 和S :: foobar是一个从属名称,在实例化模板时解析。
这就是s1.foobar
可以打印A的原因(因为此时定义了foo(A))
答案 1 :(得分:5)
由于T
是模板参数,因此表达式t
与类型有关,因此foo
是函数调用foo(t)
中的从属名称。 [temp.dep.candidate] 14.6.4.2/1说:
对于依赖于模板参数的函数调用,使用通常的查找规则(3.4.1,3.4.2,3.4.3)找到候选函数,除了:
对于使用非限定名称查找(3.4.1)或限定名称查找(3.4.3)的查找部分,只能找到模板定义上下文中的函数声明。
对于使用关联命名空间(3.4.2)的查找部分,只能找到模板定义上下文或模板实例化上下文中找到的函数声明。
如果函数名称是 unqualified-id ,并且调用结果不正确或者找到更好的匹配,则相关命名空间中的查找会考虑所有带有外部链接的函数声明所有翻译单元中的名称空间,不仅仅考虑模板定义和模板实例化上下文中的那些声明,那么程序就有未定义的行为。
实例化S<double>::foobar
时,T
显然是double
且没有关联的命名空间。因此,foo
的唯一声明是来自模板定义上下文(void foo(int)
)的声明,如第一个子弹中所述。
在实例化S<A>::foobar
时,T
为A
。来自定义上下文
foo
声明
void foo(int)
A
的关联命名空间(全局命名空间)的和:
inline void foo( A ) { std::cout << "foo(A)" << std::endl; }
inline void foo( double ) { std::cout << "foo(double)" << std::endl; }
显然void foo(A)
是最佳匹配。