【问题标题】:Overloading lookup during template instanciation在模板实例化期间重载查找
【发布时间】:2015-11-24 09:04:25
【问题描述】:

我有一段代码可以使用 icc 或 visual c++ 编译,但在我使用 gcc 或 clang 时却没有。

问题在于 gcc/clang 希望在 A<T>::bind(T& t)callBindTo(T& t) 定义之前定义 bindTo(std::string& s, const int& i) 而不仅仅是在代码实例化之前。

我的问题是:为什么 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);
}

【问题讨论】:

  • 这称为前向声明。函数声明/原型应始终在其使用之前。通常,所有函数都在源文件中包含的头文件中声明。
  • 要搜索的词是两阶段查找
  • 感谢丢失的指针 ;) stackoverflow.com/questions/6273176/…

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


【解决方案1】:

GCC 和 Clang 有正确的实现。正如 cmets 提示的那样,名称 t 在实例化期间被查找,在第二个名称查找阶段,当 T==int 时。这不会为 Argument-Dependent Lookup 引入任何额外的命名空间。因此,编译器必须在全局命名空间中查找所有bind 的声明,直到它被实际使用。

最后一部分很重要。查找的范围由模板定义的位置决定,而不是由第一个实例化的位置决定。这是一件好事。如果有 5 个实例化,并且有 5 个越来越大的重载集怎么办?您必须每次都实例化模板,重做重载决议,并可能得到 5 个不同的结果。

如果您认为这还不够糟糕,您将如何处理这种名称篡改?您将拥有callBindTo&lt;int&gt;callBindTo&lt;int&gt;,这两个实例化只能通过bind 在其实例化点的重载集来区分。

不,当前的规则是合理的。是的,Argument Dependent Lookup 可以添加额外的命名空间,这很棘手,但callBindTo&lt;std::string&gt; 的所有实例化都添加相同的命名空间std。通过这个例子,我们看到你调用你的函数bindTo而不是bind是一件好事——你几乎拖了std::bind

【讨论】:

  • 当前规则仍然存在“不同的重载集”问题,尽管它确实显着缓解了它。 [temp.point]/8 和 [temp.dep.candidate]/1 使做这种事情不正确的 NDR(或 UB,本质上是等效的)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-13
  • 1970-01-01
  • 2015-01-26
  • 2011-08-12
  • 1970-01-01
  • 2021-05-16
相关资源
最近更新 更多