5

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

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

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

4

2 回答 2

3

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

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

据我所知,在表达式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);
}

或者:

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; }
}
于 2013-09-03T18:35:33.333 回答
1

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

好的,让我们试着分解一下。

第二个示例有效,因为即使在非模板的情况下,您也希望在使用前声明的所有重载情况下一切正常。

由于 14.6.4.2/1,第一个版本有效:

对于依赖于模板参数的函数调用,如果函数名称是非限定 ID 但不是模板 ID,则使用通常的查找规则(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 将再次承担重载,所有人都会很高兴。

于 2013-09-03T18:17:34.467 回答