Невозможно использовать имя имени функции

Следующий код компилируется отлично:

#include <string>

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

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

Но когда я переименую функцию с расстояния на расстояние:

#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> >’

Почему я не могу назвать расстояние функции?

Ответы

Ответ 1

Причина в том, что существует стандартный алгоритм std:: distance, который найден ADL (зависимым от аргумента): хотя ваш вызов не соответствует пространству имен std, тип ваших аргументов a и b (т.е. std::string) живет в том же пространстве имен, что и функция std::distance (т.е. std), и поэтому std::distance() также рассматривается для разрешения перегрузки.

Если вы действительно хотите вызвать свою функцию distance() (я бы предложил вам не делать этого), вы можете либо поместить ее в свое пространство имен, а затем полностью квалифицировать имя функции при ее вызове или оставить ее в глобальном пространстве имен и вызовите его следующим образом:

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

Обратите внимание, однако, что только ADL не может привести к сбою компиляции вашей программы, если ваша реализация Стандартной библиотеки предоставляет версию SFINAE iterator_traits (более подробную информацию в fooobar.com/info/377866/... - любезно предоставлено MooingDuck).

При реализации iterator_traits, совместимой с SFINAE, ваш компилятор должен признать, что шаблон функции 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().

Однако, если ваша реализация Стандартной библиотеки не обеспечивает версию SFIFAE для iterator_traits, может произойти сбой замены в контексте, который не подходит для SFINAE, что приводит к ошибке (жесткой) компиляции.

Этот живой пример показывает вашу исходную компиляцию программы с помощью GCC 4.8.0, которая поставляется с версией libstdС++, который реализует SFINAE-дружественный iterator_traits.