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