为什么这个与重载/命名空间/模板相关的C++代码无法编译?

12

以下是一些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++行为吗?


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

6

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]表明:
对于依赖于模板参数的函数调用,如果函数名是未限定的标识符而不是模板标识符,则使用通常的查找规则(3.4.1、3.4.2)找到候选函数,但有以下几点不同:
- 对于使用未限定名称查找(3.4.1)的查找部分,只能在模板定义上下文中找到具有外部链接的函数声明。 - 对于使用相关命名空间(3.4.2)查找的查找部分,只能在模板定义上下文或模板实例化上下文中找到具有外部链接的函数声明。
由于 A::f(B::C x) 没有使用相关命名空间(即参数相关查找),因此它必须在模板定义处可见,而不仅仅在实例化点可见。

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

函数不是模板函数(即它们在前面没有template <class T>。T是模板参数。) 因此当到达模板化代码时,它们可以即时编译。


这是无关紧要的。它们将被编译。问题是,编译器应该如何解决 g 中的调用? - UncleBens
@UncleBens:(在这里评论,因为我的答案被删除了):你说得对,我对问题的理解过于简单了。我已经删除了我的答案,因为它是错误的,但是我今天剩下的时间没有时间来修复它,所以请随意接管并将其更正为您自己的答案。 :) - GManNickG
@UncleBens:有人在询问“除了模板之外的所有东西”的概念。所以我添加了这个答案,尽管评论现在似乎已经被删除了。 - Furkan Kıraç

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接