I've been badly bitten by the following code, on which I wasted many hours of precious time.
#include<string>
int next(std::string param){
return 0;
}
void foo(){
next(std::string{ "abc" });
}
This produces the following compiler error (on Visual Studio 2013):
1>------ Build started: Project: sandbox, Configuration: Debug Win32 ------
1> test.cpp
1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(371): error C2039: 'iterator_category' : is not a member of 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>'
1> c:\users\ray\dropbox\programming\c++\sandbox\test.cpp(8) : see reference to class template instantiation 'std::iterator_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>' being compiled
1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(371): error C2146: syntax error : missing ';' before identifier 'iterator_category'
1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(371): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(371): error C2602: 'std::iterator_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>::iterator_category' is not a member of a base class of 'std::iterator_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>'
1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(371) : see declaration of 'std::iterator_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>::iterator_category'
1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(371): error C2868: 'std::iterator_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>::iterator_category' : illegal syntax for using-declaration; expected qualified-name
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
I found out later that if I change my function name from next() to something else, all is fine. To me, this indicates that there is a name conflict, specifically of the name next. I find this strange because I didn't use anything like using namespace std. As far as I know, next is not a built-in C++ keyword (is it?). I looked up next here, but it's std::next and as I said I didn't using namespace std. So how did this conflict happen? How do I prevent similar things in the future? What other names might cause a conflict like this?
There are several things going on here, interacting in subtle ways.
Firstly, an unqualified call to next with an argument of type std::string means that as well as your own next function, the standard function template std::next is found by Argument-dependent lookup (ADL).
After name lookup has found your ::next and the standard library's std::next it performs overload resolution to see which one is a better match for the arguments you called it with.
The definition of std::next looks like:
template <class ForwardIterator>
ForwardIterator next(ForwardIterator x,
typename std::iterator_traits<ForwardIterator>::difference_type n = 1);
This means that when the compiler performs overload resolution it substitutes the type std::string into std::iterator_traits<std::string>.
Prior to C++14 iterator_traits is not SFINAE-friendly which means that it is invalid to instantiate it with a type that is not an iterator. std::string is not an iterator, so it's invalid. The SFINAE rule does not apply here, because the error is not in the immediate context, and so using iterator_traits<T>::difference_type for any non-iterator T will produce a hard error, not a substitution failure.
Your code should work correctly in C++14, or using a different standard library implementation that already provides a SFINAE-friendly iterator_traits, such as GCC's library. I believe Microsoft will also provide a SFINAE-friendly iterator_traits for the next major release of Visual Studio.
To make your code work now you can qualify the call to next so ADL is not performed:
::next(std::string{ "abc" });
This says to call the next in the global namespace, rather than any other next that might be found by unqualified name lookup.
(Updated per Jonathan's comments)
There are two views here, the C++11 and the C++14 view. Back in 2013, C++11's std::next was not properly defined. It is supposed to apply to iterators, but due to what looks like an oversight it will cause hard failures when you pass it a non-iterator. I believe the intention was that SFINAE should have prevented this; std::iterator_traits<X> should cause substitution failures.
In C++14, this problem is solved. The definition of std::next hasn't changed, but it's second argument (std::iterator_traits<>) is now properly empty for non-iterators. This eliminates std::next from the overload set for non-iterators.
The relevant declaration (taken from VS2013) is
template<class _FwdIt> inline
_FwdIt next(_FwdIt _First,
typename iterator_traits<_FwdIt>::difference_type _Off = 1)
This function should be added to the overload set if it can be instantiated for the given arguments.
The function is found via Argument Dependent Lookup and Microsoft's header structure. They put std::next in <xutility> which is shared between <string> and <iterator>
Note: _FwdIt and _Off are part of the implementation namespace. Don't use leading underscores yourself.
Actually std::next() is a function defined in <iterator> which returns the next iterator passed to std::next(). Your code is running on my computer with gcc-4.9.2. More : http://en.cppreference.com/w/cpp/iterator/next
The code I used:
#include<string>
#include <iostream>
int next(std::string param){
std::cout<<param<<std::endl;
return 0;
}
void foo(){
next(std::string{ "abc" });
}
int main()
{
foo();
return 0;
}
Also on ideone : http://ideone.com/QVxbO4
The compiler found standard function std::next due to the so-called Argument Dependent Lookup because the argument used in the call - std::string - is declared in namespace std.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With