命名空间中的重载决策

Dar*_*con 5 c++ overload-resolution

我试图在命名空间内调用一个重载函数,并且有点挣扎.

工作示例1:没有命名空间

class C {};

inline void overloaded(int) {}

template<typename T> void try_it(T value) {
  overloaded(value);
}

inline void overloaded(C) {}


int main()
{
  try_it(1);
  C c;
  try_it(c);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

工作示例2:模板之前定义的所有重载

class C {};

namespace n {
  inline void overloaded(int) {}
  inline void overloaded(C) {}
}

template<typename T> void try_it(T value) {
  n::overloaded(value);
}

int main()
{
  try_it(1);
  C c;
  try_it(c);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

破坏的示例3:模板后的一些重载

class C {};

namespace n {
  inline void overloaded(int) {}
}

template<typename T> void try_it(T value) {
  n::overloaded(value);
}

namespace n {
  inline void overloaded(C) {}
}

int main()
{
  try_it(1);
  C c;
  try_it(c); // /tmp/test.cpp: In function ‘void try_it(T) [with T = C]’:
             // /tmp/test.cpp:19:15:   instantiated from here
             // /tmp/test.cpp:8:7: error: cannot convert ‘C’ to ‘int’ for argument ‘1’ to ‘void n::overloaded(int)’

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

为什么会这样?在模板函数之后,我需要做什么才能声明或定义重载?

dyp*_*dyp 3

这是一个依赖名称查找的问题。

在表达式 中overloaded(value);,名称overloaded是依赖于 [temp.dep]/1 的。

据我所知,在 expression 中n::overloaded(value),名称overloaded(在id-expression n::overloaded中)是相关的。


依赖名称查找非常奇特,[temp.dep.res]/1

在解析从属名称时,会考虑以下来源的名称:

  • 在模板定义时可见的声明。
  • 来自与来自实例化上下文和定义上下文的函数参数类型相关联的命名空间的声明。

(文件末尾有一个函数模板的实例化点,因此可以找到关联命名空间的所有声明。)

对于非依赖名称,应用正常的查找规则(从定义上下文中查找)。

因此,要查找模板定义后声明的名称,它们必须是依赖的并且可以通过 ADL 找到。


一个简单的解决方法是向函数引入另一个参数overloaded或包装该参数,以便该函数的参数之一具有n关联的命名空间:

#include <iostream>

class C {};

namespace n {
  struct ADL_helper {};
  inline void overloaded(int, ADL_helper = {})
  { std::cout << "n::overloaded(int,..)" << std::endl; }
}

template<typename T> void try_it(T value) {
  overloaded(value, n::ADL_helper{});
}

namespace n {
  inline void overloaded(C, ADL_helper = {})
  { std::cout << "n::overloaded(C,..)" << std::endl; }
}

int main()
{
  try_it(1);
  C c;
  try_it(c);
}
Run Code Online (Sandbox Code Playgroud)

或者:

namespace n {
  template < typename T >
  struct wrapper { T elem; };

  inline void overloaded(wrapper<int>)
  { std::cout << "n::overloaded(wrapper<int>)" << std::endl; }
}

template<typename T> void try_it(T value) {
  overloaded(n::wrapper<T>{value});
}

namespace n {
  inline void overloaded(wrapper<C>)
  { std::cout << "n::overloaded(wrapper<C>)" << std::endl; }
}
Run Code Online (Sandbox Code Playgroud)