14

以下代码编译良好:

#include <string>

int dist(std::string& a, std::string& b) {
  return 0;
}

int main() {
  std::string a, b;
  dist(a, b);
  return 0;
}

但是当我将函数从 dist 重命名为 distance 时:

#include <string>

int distance(std::string& a, std::string& b) {
  return 0;
}

int main() {
  std::string a, b;
  distance(a, b);
  return 0;
}

编译时出现此错误(gcc 4.2.1):

/usr/include/c++/4.2.1/bits/stl_iterator_base_types.h: In instantiation of ‘std::iterator_traits<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >’:
b.cpp:9:   instantiated from here
/usr/include/c++/4.2.1/bits/stl_iterator_base_types.h:129: error: no type named ‘iterator_category’ in ‘struct std::basic_string<char, std::char_traits<char>, std::allocator<char> >’

为什么我不能命名函数距离?

4

1 回答 1

23

原因是存在一个名为std::distance的标准算法,它由 ADL(Argument Dependent Lookup)找到:虽然您的调用没有使用std命名空间限定,但您的参数类型ab(即std::string)与std::distance函数(即)std,因此std::distance()也被考虑用于重载解决方案。

如果你真的想调用你的函数distance()(我建议你不要),你可以把它放在你的命名空间中,然后在你调用它时完全限定函数名称,或者将它留在全局命名空间中并调用这样:

    ::distance(a, b);
//  ^^

但是请注意,如果您的标准库实现提供了对 SFINAE 友好的版本(更多详细信息在StackOverflow 上的问答中- 由MooingDuck提供),则单独的 ADL 可能不会导致您的程序编译失败。iterator_traits

使用 SFINAE 友好的 实现iterator_traits,您的编译器应该认识到std::distance()函数模板(因为它是模板)在给定类型参数时无法实例化std::string,因为它的返回类型:

template< class InputIt >
typename std::iterator_traits<InputIt>::difference_type 
//       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//       Trying to instantiate this with InputIt = std::string
//       may result in a soft error during type deduction if
//       your implementation is SFINAE-friendly, and in a hard
//       error otherwise.
    distance( InputIt first, InputIt last );

在这种情况下,编译器会出于重载决议的目的而简单地丢弃此模板并选择您的distance()函数。

但是,如果您的标准库实现不提供对 SFINAE 友好的版本iterator_traits,则在不符合 SFINAE 条件的上下文中可能会发生替换失败,从而导致(硬)编译错误。

这个活生生的例子展示了使用 GCC 4.8.0 编译的原始程序,它带有一个实现 SFINAE 友好的 libstdc++ 版本iterator_traits

于 2013-06-03T17:17:13.410 回答