【问题标题】:Why doesn't this overloading/namespace/template-related C++ code compile?为什么这个重载/命名空间/模板相关的 C++ 代码不能编译?
【发布时间】:2011-12-02 22:59:57
【问题描述】:

这是一些 C++ 代码:

namespace A {

int f(int x) { return 0; }
int f(long x) { return 1; }

template<class T> int g(T x) {
  return f(x);
}

}

namespace B {
struct C {};
}

namespace A {
int f(B::C x) { return 2; }
}

void h() {
  A::g(B::C());
}

在命名空间 A 中,代码声明了函数 f 的一些重载,以及调用 f 的模板函数 g。然后我们在命名空间 B 中声明一个新类型,并在命名空间 A 中为新类型重载 f。。用 g++ 4.2 编译给出了

order.cpp: In function ‘int A::g(T) [with T = B::C]’:
order.cpp:21:   instantiated from here
order.cpp:7: error: no matching function for call to ‘f(B::C&)’
order.cpp:3: note: candidates are: int A::f(int)
order.cpp:4: note:                 int A::f(long int)

如果我执行以下任何操作,代码就会起作用:

  1. 删除命名空间。
  2. 将 B::C 的 f 重载移至命名空间 B(感谢 Koenig 查找)。
  3. 将 B::C 的声明及其 f 重载移到 g() 的定义之上。

我对(3)特别困惑,因为我的印象是重载解析应该独立于声明的顺序。这是预期的 C++ 行为吗?

【问题讨论】:

  • 这是一个很好的问题,这里有三个相同的简化示例:1) 失败:ideone.com/MSQHk 2) 删除 int f(int); 并编译:ideone.com/W1jZA 3) 将模板移到顶部并再次编译:ideone.com/zbedP
  • 越来越有趣了。看起来 gcc 4.5.1 也不是没有错误。 Comeau 拒绝 2) 和 3) 示例,以及放在全局命名空间中的示例,但它确实使用 ADL 在 B 中找到 f。
  • 有趣的是,代码在 MSVS 2010 中编译并运行良好。

标签: c++ templates namespaces overloading


【解决方案1】:

Clang 给出以下错误信息,这为问题提供了一些线索:

$ clang -fsyntax-only test.cc -Wall
test.cc:7:10: error: call to function 'f' that is neither visible in the
      template definition nor found by argument-dependent lookup
  return f(x);
         ^
test.cc:21:3: note: in instantiation of function template specialization
      'A::g<B::C>' requested here
  A::g(B::C());
  ^
test.cc:17:5: note: 'f' should be declared prior to the call site or in
      namespace 'B'
int f(B::C x) { return 2; }
    ^
1 error generated.

具体来说,您已经详细了解了模板定义中依赖名称的两阶段查找。在 C++98 中,[temp.dep.candidate] 说:

对于依赖于模板参数的函数调用,如果函数名称是非限定 ID 但不是模板 ID,则使用通常的查找规则(3.4.1、3.4.2)找到候选函数,除了那:

  • 对于使用非限定名称查找 (3.4.1) 的查找部分,只能找到具有来自模板定义上下文的外部链接的函数声明。
  • 对于使用关联命名空间 (3.4.2) 的查找部分,只能找到在模板定义上下文或模板实例化上下文中找到的具有外部链接的函数声明。

由于A::f(B::C x) 没有使用关联的命名空间(即依赖于参数的查找),因此它必须在模板定义站点可见,而不仅仅是在实例化点。

【讨论】:

    【解决方案2】:

    例如

    int f(int x) { return 0; }
    int f(long x) { return 1; }
    

    函数不是模板函数(即它们前面没有template &lt;class T&gt;。T 是模板参数。) 因此,当达到模板代码时,它们可以被即时编译。

    【讨论】:

    • 这无关紧要。无论如何,它们都会被编译。问题是,编译器应该如何解析g中的调用。
    • @UncleBens:(在这里评论是因为我的答案已被删除):你说得对,我的理解过于简单化了。我已经删除了我的答案,因为它是错误的,但是我没有时间在剩下的时间里修复它,所以请随意劫持和更正它作为你自己的。 :)
    • @UncleBens:有人要求澄清“所有不是模板的东西”。所以这就是我添加这个答案的原因,尽管评论现在似乎被删除了。
    猜你喜欢
    • 2010-11-24
    • 1970-01-01
    • 2014-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多