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?
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.
Related
In the following snippet of code, I've overloaded the operator== to compare my pair type with string. But for some reason, the compiler isn't finding my operator as a match for the find function. Why not?
Edit: Thanks for all the suggestions for alternatives, but I'd still like to understand why. The code looks like it should work; I'd like to know why it doesn't.
#include <vector>
#include <utility>
#include <string>
#include <algorithm>
typedef std::pair<std::string, int> RegPair;
typedef std::vector<RegPair> RegPairSeq;
bool operator== (const RegPair& lhs, const std::string& rhs)
{
return lhs.first == rhs;
}
int main()
{
RegPairSeq sequence;
std::string foo("foo");
// stuff that's not important
std::find(sequence.begin(), sequence.end(), foo);
// g++: error: no match for 'operator==' in '__first. __gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* [with _Iterator = std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int>*, _Container = std::vector<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int>, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> > >]() == __val'
// clang++: error: invalid operands to binary expression ('std::pair<std::basic_string<char>, int>' and 'std::basic_string<char> const')
}
The problem is that std::find is a function template and it uses argument-dependent lookup (ADL) to find the right operator== to use.
Both of the arguments are in the std namespace (std::pair<std::string, int> and std::string), so ADL starts by looking in the std namespace. There it finds some operator== (which one, it doesn't matter; there are lots in the Standard Library and if you've included <string>, at least the one that compares two std::basic_string<T> objects could be found).
Because an operator== overload is found in the std namespace, ADL stops searching enclosing scopes. Your overload, which is located in the global namespace, is never found. Name lookup occurs before overload resolution; it doesn't matter during name lookup whether the arguments match.
The cleanest solution is to make a predicate and use find_if:
struct StringFinder
{
StringFinder(const std::string & st) : s(st) { }
const std::string s;
bool operator()(const RegPair& lhs) const { return lhs.first == s; }
}
std::find_if(sequence.begin(), sequence.end(), StringFinder(foo));
If you have C++11 you can use a lambda instead.
The accepted answer is, unfortunately, misleading.
Overload resolution for operator == used inside std::find function template is performed by both regular lookup and argument-dependent lookup (ADL)
Regular lookup is performed in accordance with usual rules of unqualified name lookup. It is looked up from the definition of std::find in standard library. Obviously, the above user-provided declaration of operator == is not visible from there.
ADL is a different story. Theoretically ADL can see names defined later, e.g. names visible from the point of std::find invocation inside main. However, ADL does not just see everything. ADL is restricted to searching only inside so called associated namespaces. These namespaces are brought into the consideration by types of arguments used in the invocation of operator == in accordance to the rules of 6.4.2/2.
In this example types of both arguments of == belong to namespace std. One template argument of std:pair<> is also from std. Another is of fundamental type int, which has no associated namespace. Therefore std is the only associated namespace in this case. ADL looks in std and only in std. The above user-provided declaration of operator == is not found, since it resides in global namespace.
It is incorrect to say that ADL stops looking after finding some "other" definitions of operator == inside std. ADL does not work in "inside-out" fashion as other forms of lookup often do. ADL searches in associated namespaces and that's it. Regardless of whether any other forms of operator == were found in std or not, ADL does not attempt to continue its search in global namespace. This is the incorrect/misleading part of the accepted answer.
Here's a more compact example that illustrates the same issue
namespace N
{
struct S {};
}
template<typename T> void foo(T a)
{
bar(a);
}
void bar(N::S s) {}
int main()
{
N::S a;
foo(a);
}
Ordinary lookup fails since there's no bar declared above foo. Seeing that bar is called with an argument of N::S type, ADL will look for bar in associated namespace N. There's no bar in N either. The code is ill-formed. Note that absense of bar in N does not make ADL to expand its search into the global namespace and find global bar.
It is quite easy to inadvertently change the set of associated namespaces used by ADL, which is why such issues often come and go after seemingly innocent and unrelated changes in the code. For example, if we change the declaration of RegPair as follows
enum E { A, B, C };
typedef std::pair<std::string, E> RegPair;
the error will suddenly disappear. After this change global namespace also becomes associated for ADL, along with std, which is why ADL finds the user-provided declaration of operator ==.
Another "correct" solution:
struct RegPair : std::pair<std::string, int>
{
bool operator== (const std::string& rhs) const;
};
bool RegPair::operator== (const std::string& rhs) const
{
return first == rhs;
}
I'm trying to overload operator[] for an std::map instance, and seriously puzzled by the compilation errors from GCC.
The following example will not compile:
typedef std::map< int*, int > mymap;
namespace std {
template <>
int & mymap::operator[]( const int* & k) {
return begin()->second;
};
};
This one fails with:
error: template-id 'operator[]<>' for 'int& std::map, std::allocator > >::operator[](const int*&)' does not match any template declaration
But if you replace int* with myintp (typedef int* myintp) it will compile just fine.
It's also interesting why template<> and namespace are needed here.
Update:
I oversimplified the example.
It is allowed to add template specializations for any standard library
template to the namespace std only if the declaration depends on a
user-defined type and the specialization satisfies all requirements
for the original template, except where such specializations are
prohibited.
typedef std::map< myclass*, int > mymap;
namespace std {
template <>
int & mymap::operator[]( myclass* const & k) {
return begin()->second;
};
};
Would that example provide a legal and predictable behavior?
From 17.6.4.2.1/2:
The behavior of a C++ program is undefined if it declares
— an
explicit specialization of any member function of a standard library
class template, or
So right there all bets are off and the compiler has no obligation to compile your code (other answers show why the compiler appears to accept the code that uses the typedef, but that still doesn't make it legal).
C++98 is slightly less explicit, in 17.4.3.1/1:
It is undefined for a C++ program to add declarations or definitions
to namespace std or namespaces within namespace std unless otherwise
specified. A program may add template specializations for any standard
library template to namespace std. Such a specialization (complete or
partial) of a standard library template results in undefined behavior
unless the declaration depends on a user-defined name of external
linkage and unless the specialization meets the standard library
requirements for the original template.
Now, it's somewhat unclear if this strictly prohibits specializing a member of a template of std namespace (vs a full or partial specialization of the container template itself) but your particular code is certainly undefined because it doesn't specialize on a user-defined name of external linkage.
The key type is int*.
In order to match
T& operator[]( const Key& key );
you need to use:
int & mymap::operator[]( int* const & k) {
Unfortunately, use of const before the type is confusing. Had the declaration been
T& operator[]( Key const& key );
it would have been easier to come up with the right argument declaration for what you are trying.
Let's analyze your code:
int& mymap::operator[](const int* &k)
{
return begin ()->second;
};
This is wrong since the parameter is a reference to a pointer to a const int, not a reference to a const pointer to an int.
The position of const have confused you, so rewrite it as:
int& mymap::operator[](int *const &k)
{
return begin ()->second;
};
As described in another message of mine, it is not possible to compare 2 pointers to member functions with "<" (less than). Or at least, this causes undefined behavior.
I have just managed to compile this code both with Visual C++ as well as GCC:
template <class Receiver, class Param = void*, class Return = void>
class EventReceiver : public IFunction<> {
protected:
std::set< Return(Receiver::*)(Param) > test;
std::set< Return(*)(Param) > test2;
...
AFAIK, to make a std::map or std::set of anything, it must be possible to compare the set's values with "<". Does this mean that the above containers or the actual compilers have a working implementation of comparing pointers-to-methods in such a way?
Well, it was in reality misleading that the example code compiled. The truth is that the sets are unusable. Every attempt to insert data into them produces the expected error.
That's the "dark side" of C++ template functions. They don't exist till you use them (and thus won't produce compiler errors till you do).
Check this out :
#include <set>
class X {};
int main() {
typedef void(X::*FuncPtr)();
std::set< FuncPtr > set;
std::less< FuncPtr > less;
FuncPtr f1;
FuncPtr f2;
//set.insert(f1); // both of these lines
//less(f1,f2); // produce an error
};
Removing the comments in any of the last 2 lines produces the error :
invalid operands of types ‘void (X::* const)()’ and ‘void (X::*
const)()’ to binary ‘operator<’
Compile it online yourself here.
std::set<T> uses std::less<T>, instead of using the less-than operator directly. When T is a pointer type, std::less<T> is guaranteed to be a total order, even if the less-than operator does not.
Edit: Section 20.3.3 of the C++98 standard says "pointer types", which (I think) includes pointers to functions, but not pointers to members.
In the following snippet of code, I've overloaded the operator== to compare my pair type with string. But for some reason, the compiler isn't finding my operator as a match for the find function. Why not?
Edit: Thanks for all the suggestions for alternatives, but I'd still like to understand why. The code looks like it should work; I'd like to know why it doesn't.
#include <vector>
#include <utility>
#include <string>
#include <algorithm>
typedef std::pair<std::string, int> RegPair;
typedef std::vector<RegPair> RegPairSeq;
bool operator== (const RegPair& lhs, const std::string& rhs)
{
return lhs.first == rhs;
}
int main()
{
RegPairSeq sequence;
std::string foo("foo");
// stuff that's not important
std::find(sequence.begin(), sequence.end(), foo);
// g++: error: no match for 'operator==' in '__first. __gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* [with _Iterator = std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int>*, _Container = std::vector<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int>, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> > >]() == __val'
// clang++: error: invalid operands to binary expression ('std::pair<std::basic_string<char>, int>' and 'std::basic_string<char> const')
}
The problem is that std::find is a function template and it uses argument-dependent lookup (ADL) to find the right operator== to use.
Both of the arguments are in the std namespace (std::pair<std::string, int> and std::string), so ADL starts by looking in the std namespace. There it finds some operator== (which one, it doesn't matter; there are lots in the Standard Library and if you've included <string>, at least the one that compares two std::basic_string<T> objects could be found).
Because an operator== overload is found in the std namespace, ADL stops searching enclosing scopes. Your overload, which is located in the global namespace, is never found. Name lookup occurs before overload resolution; it doesn't matter during name lookup whether the arguments match.
The cleanest solution is to make a predicate and use find_if:
struct StringFinder
{
StringFinder(const std::string & st) : s(st) { }
const std::string s;
bool operator()(const RegPair& lhs) const { return lhs.first == s; }
}
std::find_if(sequence.begin(), sequence.end(), StringFinder(foo));
If you have C++11 you can use a lambda instead.
The accepted answer is, unfortunately, misleading.
Overload resolution for operator == used inside std::find function template is performed by both regular lookup and argument-dependent lookup (ADL)
Regular lookup is performed in accordance with usual rules of unqualified name lookup. It is looked up from the definition of std::find in standard library. Obviously, the above user-provided declaration of operator == is not visible from there.
ADL is a different story. Theoretically ADL can see names defined later, e.g. names visible from the point of std::find invocation inside main. However, ADL does not just see everything. ADL is restricted to searching only inside so called associated namespaces. These namespaces are brought into the consideration by types of arguments used in the invocation of operator == in accordance to the rules of 6.4.2/2.
In this example types of both arguments of == belong to namespace std. One template argument of std:pair<> is also from std. Another is of fundamental type int, which has no associated namespace. Therefore std is the only associated namespace in this case. ADL looks in std and only in std. The above user-provided declaration of operator == is not found, since it resides in global namespace.
It is incorrect to say that ADL stops looking after finding some "other" definitions of operator == inside std. ADL does not work in "inside-out" fashion as other forms of lookup often do. ADL searches in associated namespaces and that's it. Regardless of whether any other forms of operator == were found in std or not, ADL does not attempt to continue its search in global namespace. This is the incorrect/misleading part of the accepted answer.
Here's a more compact example that illustrates the same issue
namespace N
{
struct S {};
}
template<typename T> void foo(T a)
{
bar(a);
}
void bar(N::S s) {}
int main()
{
N::S a;
foo(a);
}
Ordinary lookup fails since there's no bar declared above foo. Seeing that bar is called with an argument of N::S type, ADL will look for bar in associated namespace N. There's no bar in N either. The code is ill-formed. Note that absense of bar in N does not make ADL to expand its search into the global namespace and find global bar.
It is quite easy to inadvertently change the set of associated namespaces used by ADL, which is why such issues often come and go after seemingly innocent and unrelated changes in the code. For example, if we change the declaration of RegPair as follows
enum E { A, B, C };
typedef std::pair<std::string, E> RegPair;
the error will suddenly disappear. After this change global namespace also becomes associated for ADL, along with std, which is why ADL finds the user-provided declaration of operator ==.
Another "correct" solution:
struct RegPair : std::pair<std::string, int>
{
bool operator== (const std::string& rhs) const;
};
bool RegPair::operator== (const std::string& rhs) const
{
return first == rhs;
}
template<class A,class B>
void tmp(){
set<int,int>::iterator it; //works
set<A,B>::iterator it; // doesn't work
}
Due to some rather annoying limitations in C++'s grammar, you must explicitly tell C++ that set<A,B>::iterator is a type name, rather than a static member identifier, using the typename keyword. For example, this code compiles just fine:
#include <set>
template<class A, class B>
void tmp() {
std::set<int,int>::iterator x; // OK
typename std::set<A,B>::iterator it; // Also OK
}
int main() {
tmp<int,int>();
return 0;
}
This occurs because C++ requires that the compiler make a final decision as to whether to interpret set<A,B>::iterator as a type or as a variable/function when it's parsing the grammar; before the template is instantiated. However, prior to template instantiation, it is impossible to make that determination, as in the general case this may depend on the values of A and B. As such, the compiler will assume it to be a variable/function unless explicitly stated otherwise. This then results in a parse error.