我知道依赖名称,必须将绑定推迟到实例化点。 请考虑以下代码:
#include <iostream>
using namespace std;
template <class T>
void g(T a)
{
f(a);
}
namespace MyNamespace
{
class X{};
void f(X) { cout << "f(X)\n"; }
}
void f(int i) { cout << "f(int)\n"; }
int main()
{
g(MyNamespace::X{}); //1. print f(x)
//g(1); //2. not compile
return 0;
}
为1,我测试的所有编译器(VC ++,gcc,clang)都可以通过ADL找到MyNamespace :: f(X)。没关系,这符合标准。但是,gcc和clang无法编译2.因为标准不允许直接在实例化上下文中搜索函数。这是标准要求(Fine,vc ++违反了这方面的标准)。
我知道标准不允许在模板实例化环境中搜索功能是ODR的安全措施。但是,为什么它允许ADL检查从模板实例化上下文中可见的函数声明?这会导致违反ODR吗?请考虑以下内容:
//ff.h
#include <iostream>
namespace N
{
class X {};
int g(X, int i);
}
template<typename T>
double ff(T t, double d)
{
return g(t, d);
}
//ff.cpp
#include "ff.h"
int N::g(X, int i) { std::cout << "g(X,int)" << std::endl; return i; }
double x1 = ff(N::X{}, 1.1);
//main.cpp
#include "ff.h"
namespace N
{
double g(X, double d) { std::cout << "g(X,double)" << std::endl; return d; }
}
auto x2 = ff(N::X{}, 2.2);
int main()
{
extern double x1;
std::cout<<"x1 = " << x1 << std::endl;
std::cout << "x2 = " << x2 << std::endl;
return 0;
}
有两个地方实例化函数模板ff:ff(N::X, double)
,一个在ff.cpp中,一个在main.cpp中。在main.cpp中,命名空间被更改,并且在实例化ff之前在main.cpp中添加了更好的匹配函数double g(X, double)
。因此,main.cpp中的实例化可以获得这个更好的匹配函数g
,无论如何g
在main.cpp的范围内是相等的。但是ff.cpp中的实例化只能选择ff.h中声明的第一个g
。这显然违反了ODR,虽然我测试了所有3个编译器选择了int g(X, int)
,但这似乎编译器足够聪明,可以在生成的代码中检测到这种疾病,并优化了重复的专业化。编制者没有义务发现这类问题。那么为什么标准允许在这种情况下使用ADL,这可能会导致对ODR的潜在违规?