命名空间中的重载决策

时间:2013-09-03 18:12:14

标签: 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;
}

工作示例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;
}

破解示例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;
}

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

2 个答案:

答案 0 :(得分:3)

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

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

据我所知,在n::overloaded(value)表达式中,名称overloaded(在 id-expression n::overloaded中)不是< / strong>依赖。


依赖名称查找非常特殊,[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);
}

或者:

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; }
}

答案 1 :(得分:1)

来自原始版本的巨大编辑,我错误地认为该调用是非依赖名称。

好的,我们试着打破这个。

第二个例子有效,因为即使在非模板的情况下,你也希望在使用之前声明所有重载都能正常工作。

第一个版本因14.6.4.2/1:

而起作用
  

对于依赖于模板参数的函数调用,如果是   函数名是一个不合格的id,但不是一个templateid,候选者   使用通常的查找规则(3.4.1,3.4.2)找到函数,除了   的是:

     

- 对于使用非限定名称查找的查找部分   (3.4.1),只有函数声明与外部链接   找到模板定义上下文。

     

- 对于查找的部分   使用关联的命名空间(3.4.2),只使用函数声明   在模板定义上下文中找到的外部链接   找到模板实例化上下文。

我们特别感兴趣的是第一部分if the function name is an unqualified-id和第二部分found in either the template definition context or the template instantiation context are found.所以我们知道如果名称不合格,则在实例化时可见的名称会被添加到候选集合中。

同样在第三种情况下,你的名字是完全合格的,因此禁止使用在实例化时可见的候选人,而只是在定义点落后于候选人。

如果您要将c移至n,请将using namespace n;添加到try函数中,然后从您找到的函数调用中删除n:: ADL会再次过载,所有人都会很高兴。