I tried the code from this question C++ std::transform() and toupper() ..why does this fail?
#include <iostream>
#include <algorithm>
int main() {
std::string s="hello";
std::string out;
std::transform(s.begin(), s.end(), std::back_inserter(out), std::toupper);
std::cout << "hello in upper case: " << out << std::endl;
}
Theoretically it should've worked as it's one of the examples in Josuttis' book, but it doesn't compile http://ideone.com/aYnfv.
Why did GCC complain:
no matching function for call to ‘transform(
__gnu_cxx::__normal_iterator<char*, std::basic_string
<char, std::char_traits<char>, std::allocator<char> > >,
__gnu_cxx::__normal_iterator<char*, std::basic_string
<char, std::char_traits<char>, std::allocator<char> > >,
std::back_insert_iterator<std::basic_string
<char, std::char_traits<char>, std::allocator<char> > >,
<unresolved overloaded function type>)’
Am I missing something here? Is it GCC related problem?
Just use ::toupper instead of std::toupper. That is, toupper defined in the global namespace, instead of the one defined in std namespace.
std::transform(s.begin(), s.end(), std::back_inserter(out), ::toupper);
Its working : http://ideone.com/XURh7
Reason why your code is not working : there is another overloaded function toupper in the namespace std which is causing problem when resolving the name, because compiler is unable to decide which overload you're referring to, when you simply pass std::toupper. That is why the compiler is saying unresolved overloaded function type in the error message, which indicates the presence of overload(s).
So to help the compiler in resolving to the correct overload, you've to cast std::toupper as
(int (*)(int))std::toupper
That is, the following would work:
//see the last argument, how it is casted to appropriate type
std::transform(s.begin(), s.end(), std::back_inserter(out),(int (*)(int))std::toupper);
Check it out yourself: http://ideone.com/8A6iV
Problem
std::transform(
s.begin(),
s.end(),
std::back_inserter(out),
std::toupper
);
no matching function for call to ‘transform(__gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::back_insert_iterator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, <unresolved overloaded function type>)’
This is a misleading error; the interesting part is not that there's "no matching function" for the call, but why there's no matching function.
The why is that you're passing a function reference of an "<unresolved overloaded function type>" as an argument, and GCC prefers to error on the call rather than on this overload resolution failure.
Explanation
First, you should consider how the C library is inherited in C++. <ctype.h> has a function int toupper(int).
C++ inherits this:
[n3290: 21.7/1]: Tables 74, 75, 76, 77, 78, and 79 describe headers
<cctype>, <cwctype>, <cstring>, <cwchar>, <cstdlib>
(character conversions), and <cuchar>, respectively.
[n3290: 21.7/2]: The contents of these headers shall be the same as
the Standard C Library headers <ctype.h>, <wctype.h>,
<string.h>, <wchar.h>, and <stdlib.h> and the C Unicode TR
header <uchar.h>, respectively [..]
[n3290: 17.6.1.2/6]:Names that are defined as functions in C shall
be defined as functions in the C++ standard library.
But using <ctype.h> is deprecated:
[n3290: C.3.1/1]: For compatibility with the Standard C library, the
C++ standard library provides the 18 C headers (D.5), but their use is
deprecated in C++.
And the way to access the C toupper is through the C++ backwards-compatibility header <cctype>. For such headers, the contents are either moved or copied (depending on your implementation) into the std namespace:
[n3290: 17.6.1.2/4]: [..] In the C++ standard library, however, the declarations
(except for names which are defined as macros in C) are within
namespace scope (3.3.6) of the namespace std. It is unspecified
whether these names are first declared within the global namespace
scope and are then injected into namespace std by explicit
using-declarations (7.3.3).
But the C++ library also introduces a new, locale-specific function template in header <locale>, that's also called toupper (of course, in namespace std):
[n3290: 22.2]: [..] template <class charT> charT toupper(charT c,
const locale& loc); [..]
So, when you use std::toupper, there are two overloads to choose from. Since you didn't tell GCC which function you wish to use, the overload cannot be resolved and your call to std::transform cannot be completed.
Disparity
Now, the OP of that original question did not run into this problem. He likely did not have the locale version of std::toupper in scope, but then again you didn't #include <locale> either!
However:
[n3290: 17.6.5.2]: A C++ header may include other C++ headers.
So it just so happens that either your <iostream> or your <algorithm>, or headers that those headers include, or headers that those headers include (etc), lead to the inclusion of <locale> on your implementation.
Solution
There are two workarounds to this.
You may provide a conversion clause to coerce the function pointer into referring to the overload that you wish to use:
std::transform(
s.begin(),
s.end(),
std::back_inserter(out),
(int (*)(int))std::toupper // specific overload requested
);
You may remove the locale version from the overload set by explicitly using the global toupper:
std::transform(
s.begin(),
s.end(),
std::back_inserter(out),
::toupper // global scope
);
However, recall that whether or not this function in <cctype> is available is unspecified ([17.6.1.2/4]), and using <ctype.h> is deprecated ([C.3.1/1]).
Thus, this is not the option that I would recommend.
(Note: I despise writing angle brackets as if they were part of header names — they are part of #include syntax, not header names — but I've done it here for consistency with the FDIS quotes; and, to be honest, it is clearer...)
std::transform(s.begin(), s.end(), s.begin(),
std::bind(&std::toupper<char>, std::placeholders::_1, std::locale()));
If you use vc toolchain,please include locale
Related
I tried the code from this question C++ std::transform() and toupper() ..why does this fail?
#include <iostream>
#include <algorithm>
int main() {
std::string s="hello";
std::string out;
std::transform(s.begin(), s.end(), std::back_inserter(out), std::toupper);
std::cout << "hello in upper case: " << out << std::endl;
}
Theoretically it should've worked as it's one of the examples in Josuttis' book, but it doesn't compile http://ideone.com/aYnfv.
Why did GCC complain:
no matching function for call to ‘transform(
__gnu_cxx::__normal_iterator<char*, std::basic_string
<char, std::char_traits<char>, std::allocator<char> > >,
__gnu_cxx::__normal_iterator<char*, std::basic_string
<char, std::char_traits<char>, std::allocator<char> > >,
std::back_insert_iterator<std::basic_string
<char, std::char_traits<char>, std::allocator<char> > >,
<unresolved overloaded function type>)’
Am I missing something here? Is it GCC related problem?
Just use ::toupper instead of std::toupper. That is, toupper defined in the global namespace, instead of the one defined in std namespace.
std::transform(s.begin(), s.end(), std::back_inserter(out), ::toupper);
Its working : http://ideone.com/XURh7
Reason why your code is not working : there is another overloaded function toupper in the namespace std which is causing problem when resolving the name, because compiler is unable to decide which overload you're referring to, when you simply pass std::toupper. That is why the compiler is saying unresolved overloaded function type in the error message, which indicates the presence of overload(s).
So to help the compiler in resolving to the correct overload, you've to cast std::toupper as
(int (*)(int))std::toupper
That is, the following would work:
//see the last argument, how it is casted to appropriate type
std::transform(s.begin(), s.end(), std::back_inserter(out),(int (*)(int))std::toupper);
Check it out yourself: http://ideone.com/8A6iV
Problem
std::transform(
s.begin(),
s.end(),
std::back_inserter(out),
std::toupper
);
no matching function for call to ‘transform(__gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::back_insert_iterator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, <unresolved overloaded function type>)’
This is a misleading error; the interesting part is not that there's "no matching function" for the call, but why there's no matching function.
The why is that you're passing a function reference of an "<unresolved overloaded function type>" as an argument, and GCC prefers to error on the call rather than on this overload resolution failure.
Explanation
First, you should consider how the C library is inherited in C++. <ctype.h> has a function int toupper(int).
C++ inherits this:
[n3290: 21.7/1]: Tables 74, 75, 76, 77, 78, and 79 describe headers
<cctype>, <cwctype>, <cstring>, <cwchar>, <cstdlib>
(character conversions), and <cuchar>, respectively.
[n3290: 21.7/2]: The contents of these headers shall be the same as
the Standard C Library headers <ctype.h>, <wctype.h>,
<string.h>, <wchar.h>, and <stdlib.h> and the C Unicode TR
header <uchar.h>, respectively [..]
[n3290: 17.6.1.2/6]:Names that are defined as functions in C shall
be defined as functions in the C++ standard library.
But using <ctype.h> is deprecated:
[n3290: C.3.1/1]: For compatibility with the Standard C library, the
C++ standard library provides the 18 C headers (D.5), but their use is
deprecated in C++.
And the way to access the C toupper is through the C++ backwards-compatibility header <cctype>. For such headers, the contents are either moved or copied (depending on your implementation) into the std namespace:
[n3290: 17.6.1.2/4]: [..] In the C++ standard library, however, the declarations
(except for names which are defined as macros in C) are within
namespace scope (3.3.6) of the namespace std. It is unspecified
whether these names are first declared within the global namespace
scope and are then injected into namespace std by explicit
using-declarations (7.3.3).
But the C++ library also introduces a new, locale-specific function template in header <locale>, that's also called toupper (of course, in namespace std):
[n3290: 22.2]: [..] template <class charT> charT toupper(charT c,
const locale& loc); [..]
So, when you use std::toupper, there are two overloads to choose from. Since you didn't tell GCC which function you wish to use, the overload cannot be resolved and your call to std::transform cannot be completed.
Disparity
Now, the OP of that original question did not run into this problem. He likely did not have the locale version of std::toupper in scope, but then again you didn't #include <locale> either!
However:
[n3290: 17.6.5.2]: A C++ header may include other C++ headers.
So it just so happens that either your <iostream> or your <algorithm>, or headers that those headers include, or headers that those headers include (etc), lead to the inclusion of <locale> on your implementation.
Solution
There are two workarounds to this.
You may provide a conversion clause to coerce the function pointer into referring to the overload that you wish to use:
std::transform(
s.begin(),
s.end(),
std::back_inserter(out),
(int (*)(int))std::toupper // specific overload requested
);
You may remove the locale version from the overload set by explicitly using the global toupper:
std::transform(
s.begin(),
s.end(),
std::back_inserter(out),
::toupper // global scope
);
However, recall that whether or not this function in <cctype> is available is unspecified ([17.6.1.2/4]), and using <ctype.h> is deprecated ([C.3.1/1]).
Thus, this is not the option that I would recommend.
(Note: I despise writing angle brackets as if they were part of header names — they are part of #include syntax, not header names — but I've done it here for consistency with the FDIS quotes; and, to be honest, it is clearer...)
std::transform(s.begin(), s.end(), s.begin(),
std::bind(&std::toupper<char>, std::placeholders::_1, std::locale()));
If you use vc toolchain,please include locale
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;
}
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.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
unresolved overloaded function type c++
Consider the code snippet below:
#include <algorithm>
#include <cctype>
#include <string>
using namespace std;
void test(){
std::string str = "Hello World!";
std::transform(str.begin(), str.end(), str.begin(), tolower);
}
There is an error about tolower when compiling with G++: unresolved overloaded function.
If using namespace std; is removed, the code works fine.
Then, my questions are:
What is the relationship between namespace std with C functions?
What is the difference between #include<ctype.h> and #include<cctype>? Although both of them don't work in the example above.
Why std::tolower also doesn't work? What's the difference between std::tolower and tolower?
There is no relationship between namespace std and C functions. But
your code isn't C, it's C++, so you also have to consider C++ functions.
Like std::tolower, for example, in <locale>. Your problem is due to
a concurrence of things:
One of the headers you include includes <locale>. C++ headers are
allowed to include other C++ headers, and which header includes which
other header may vary from one implementation to the other, so the
code you've written might compile with one compiler, and not with
another.
You're trying to pass the function as a pointer to function argument,
to a function template where the argument is a template type
parameter. Simply put, in order to do overload resolution on
tolower here, the compiler must match it to the type of the
argument, and in order to know the type of the argument, the compiler
must do template type deduction based on the exact type of the
function, which it can only know once it has done overload resolution.
If you want the function in <ctype.h> (which you don't, since it would
result in undefined behavior), you can get it either by including
<ctype.h> (which guarantees that it is present in the global
namespace) and using ::tolower, or by explicitly specifying the
overload you want, e.g. static_cast<int (*)(int)>( tolower ) (In this
particular case, static_cast doesn't mean type conversion, but
explicit overload resolution.)
In practice, of course, you don't do this sort of thing. If you're
doing any text processing at all, you'll define all of the necessary
functions as functional object types, which avoid undefined behavior by
either converting the input to unsigned char:
struct ToLower
{
char operator()( char ch ) const
{
return ::tolower( static_cast<unsigned char>( ch ) );
}
};
or by using the functions in <locale> which do work with char:
class ToLower
{
std::locale myLocale; // necessary to guarantee the lifetime of the facet.
std::ctype const* myCType;
public:
ToLower( std::locale const& loc = std::locale() )
; myLocal( loc )
, myCType( &std::use_facet<std::ctype>( loc ) )
{
}
bool operator()( char ch ) const
{
return myCType->tolower( ch );
}
};
Finally, WRT your second question: the difference depends on the version
of C++ you're using and the compiler. Globally, however: <ctype.h>
will introduce the functions into the global namespace; <cctype> will
introduce them into the namespace std::, and maybe (or maybe not) into
the global namespace. (And your third question has already been
answered above: std::tolower refers to a set of overloaded functions
defined in <locale> and <cctype>; ::tolower refers to a single
function defined in <ctype.h>, and just tolower is the equivalent of
::tolower, unless you've done using namespace std, in which case,
it will refer to the overload set of all of the functions mentionned
above.
You can use the namespace operator to use the C version of tolower like this:
::tolower(); // nothing before '::' means look in the global namespaece.
This will force the compiler to look for a function that is not inside a specific namespace, which is the case for all C-based API.
As for why std::tolowerdoesn't work, I have no idea. The example of cpp reference doesn't use std::transform, this might be the reason.
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;
}