我有一段使用icc或visual c ++编译的代码,但在使用gcc或clang时却没有。
问题来自于gcc / clang希望在bindTo(std::string& s, const int& i)
或A<T>::bind(T& t)
定义之前定义callBindTo(T& t)
而不是在代码实例化之前定义。
我的问题是:为什么Visual和icc不需要它?哪个编译器在标准方面具有正确的行为?
#include <string>
template <typename T>
class A
{
public:
static void bind(T& t);
};
template <typename T>
void A<T>::bind(T& t)
{
std::string s;
bindTo(s, t);
}
template <typename T>
void callBindTo(T& t)
{
std::string s;
bindTo(s, t);
}
void bindTo(std::string& s, const int& i)
{
s = i;
}
int main()
{
int i;
A<int>::bind(i);
callBindTo(i);
}
答案 0 :(得分:2)
GCC和Clang的实施正确。在注释提示时,在实例化期间查找名称t
,在第二个名称查找阶段,T==int
时查找。这不会为Argument-Dependent Lookup引入任何其他名称空间。因此,编译器必须在全局命名空间中查找bind
的所有声明,直到它实际使用的位置。
最后一部分很重要。查找的范围由模板定义的位置决定,而不是由第一个实例的位置决定。这是一件好事。如果有5个实例,有5个越来越大的过载集怎么办?您必须每次都实例化模板,重做重载决策,并可能获得5个不同的结果。
如果你觉得这不够糟糕,你会怎么处理这个名字?您有callBindTo<int>
和callBindTo<int>
两个实例,只能在实例化时通过bind
的重载集来区分。
不,目前的规则是理智的。是的,Argument Dependent Lookup可以添加额外的命名空间,这很棘手,但callBindTo<std::string>
的所有实例都添加了相同的命名空间std
。通过该示例,我们看到您调用函数bindTo
而非bind
是件好事 - 您几乎拖入了std::bind
。