在模板实例化期间重载查找

时间:2015-11-24 09:04:25

标签: c++ visual-c++ gcc clang icc

我有一段使用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);
}

1 个答案:

答案 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