Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't use function name distance

Tags:

c++

The following code compiles fine:

#include <string>

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

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

But when I rename the function from dist to distance:

#include <string>

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

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

I get this error when compiling (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> >’

Why can't I name the function distance?

like image 781
egoard Avatar asked Jun 03 '13 17:06

egoard


1 Answers

The reason is that a standard algorithm called std::distance exists, which is found by ADL (Argument Dependent Lookup): although your call is not qualified with the std namespace, the type of your arguments a and b (i.e. std::string) lives in the same namespace as the std::distance function (i.e. std), and therefore std::distance() is also considered for overload resolution.

If you really want to call your function distance() (I'd suggest you not to), you can either put it in a namespace of yours, and then fully qualify the function name when you call it, or leave it in the global namespace and invoke it this way:

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

Notice, however, that ADL alone might not cause your program to fail compiling if your implementation of the Standard Library provides a SFINAE-friendly version of iterator_traits (more details in this Q&A on StackOverflow - courtesy of MooingDuck).

With a SFINAE-friendly implementation of iterator_traits, your compiler should recognize that the std::distance() function template (because it is a template) cannot be instantiated when given arguments of type std::string, because of its return type:

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

In this case, the compiler would simply discard this template for the purposes of overload resolution and pick your distance() function.

However, if your implementation of the Standard Library does not provide a SFINAE-friendly version of iterator_traits, substitution failure may occur in a context that does not qualify for SFINAE, thus resulting in a (hard) compilation error.

This live example shows your original program compiling with GCC 4.8.0, which comes with a version of libstdc++ that implements a SFINAE-friendly iterator_traits.

like image 178
Andy Prowl Avatar answered Oct 26 '22 21:10

Andy Prowl