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

Why doesn't this overloading/namespace/template-related C++ code compile?

本文关键字:代码 C++ 编译 重载 命名空间 为什么      更新时间:2023-10-16

下面是一些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. 将f for B::C的重载移到命名空间B中(由于Koenig查找)。
  3. 将B::C的声明及其f重载移到g()的定义之上。

我对(3)特别困惑,因为我一直以为重载解析应该与声明的顺序无关。这是预期的c++行为吗?

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. deep .candidate]表示:

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

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

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

例如

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

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