在“Inside the C ++ Object Model”的第7章中,有人写道,nomember名称的解析取决于name的使用是否与“用于实例化模板的参数类型”有关。我写了一个测试:
/// -------------Test.h---------------
#ifndef TEST_H
#define TEST_H
#include <iostream>
using namespace std;
extern double foo(double t);
template <typename T>
class Test {
public:
void fun1() {
member = foo(val);
}
T fun2() {
return foo(member);
}
private:
int val;
T member;
};
#endif
和
/// -------------test1.cc-------------
#include <iostream>
using namespace std;
double foo(double t) {
cout << "foo doule is called" << endl;
return t;
}
int foo(int t) {
cout << "foo int is called" << endl;
return t;
}
-------------test.cc--------------
#include "Test.h"
extern int foo(int t);
int main() {
Test<int> fi;
fi.fun1();
fi.fun2();
return 0;
}
我希望“foo double被称为\ n foo int被称为”,但我得到了“foo double被称为\ n foo double被称为”。我的g ++版本如下。如果你能帮助我,我会很感激的。
我担心这本书不是描绘完整的画面(并且它有点老化)。是的,foo(member)
是一个依赖于模板参数的函数调用。但是在[temp.dep.candidate]的C ++标准中描述了在模板中查找函数的具体方法:
对于postfix-expression是从属名称的函数调用,使用通常的查找规则([basic.lookup.unqual],[basic.lookup.argdep])找到候选函数,除了:
- 对于使用非限定名称查找的查找部分,仅找到模板定义上下文中的函数声明。
- 对于使用关联命名空间([basic.lookup.argdep])查找的部分,仅找到在模板定义上下文或模板实例化上下文中找到的函数声明。
foo
的重载可以通过两种方式之一查找。通过直接不合格的查找,并通过argument dependent lookup(又名ADL)。简单的非限定查找仅考虑模板定义点处已知的名称。由于您只声明了foo(double)
,这是在模板定义点找到的唯一重载。
在实例化时,编译器将尝试执行ADL以查找更多foo
,但基本类型不会对ADL有所贡献。 int
不能用来寻找foo(int)
。所以编译器只能做一件事,将整数转换为double,并调用foo(double)
。
如果要测试编译器ADL,只需添加一个简单的用户定义类型和重载。例如:
enum E{};
E foo(E) {
cout << "foo E is called\n";
return {};
}
int main() {
Test<E> fi;
fi.fun1();
fi.fun2();
return 0;
}