This program
// main.cpp
#include <iostream>
#include <utility>
#include <algorithm>
#include <iterator>
#include <map>
template<typename t1, typename t2>
std::ostream& operator<<(std::ostream& os, const std::pair<t1, t2>& pair)
{
return os << "< " << pair.first << " , " << pair.second << " >";
}
int main()
{
std::map<int, int> map = { { 1, 2 }, { 2, 3 } };
std::cout << *map.begin() << std::endl;//This works
std::copy(
map.begin(),
map.end(),
std::ostream_iterator<std::pair<int,int> >(std::cout, " ")
); //this doesn't work
}
produces an error
no match for ‘operator<<’ (operand types are ‘std::ostream_iterator<std::pair<int, int> >::ostream_type {aka std::basic_ostream<char>}’ and ‘const std::pair<int, int>’)
I guess this isn't working because my overload isn't available inside std::copy
, but why is that?
Since operator<<
is being called in an unqualified manner from inside namespace std (more specifically inside std::ostream_iterator), and all the arguments involved are also declared in the same namespace, only namespace std
will be searched for potential matches.
namespace std {
template<typename t1, typename t2>
std::ostream& operator<<(std::ostream& os, const std::pair<t1, t2>& pair)
{
return os << "< " << pair.first << " , " << pair.second << " >";
}
}
Note: You can only specialize templates that includes user-defined types inside namespace std
, the above snippet is therefore potentially ill-formed according to the Standard (if std::pair<T1,T2>
isn't a user-declared type, see this discussion).
Below we have namespace N which will aid us in trying to simulate your usage of namespace std, and what is going on when the compiler tries to find a suitable overload for a given type.
namespace N
namespace N {
struct A { };
struct B { };
void func (A value) { std::cout << "A"; }
template<class T>
void call_func (T value) { func (value); }
}
main.cpp
void func (N::B value) {
std::cout << "B";
}
int main() {
N::A a;
N::B b;
func (a); // (1)
func (b); // (2)
N::call_func (a); // (3a)
N::call_func (b); // (3b)
}
Notes:
Without knowing about Argument-dependent lookup, one might be surprised that the compiler is able to find the suitable overload required to make (1) work.
ADL states that upon using an unqualified-name in a function call, not only is the current namespace searched for suitable overloads, the namespace of the arguments are also searched; and this is how the compiler finds N::func
, even though we didn't write so explicitly.
We have a suitable overload in the current namespace; it's all good in the hood.
...
Why does (3a) compile, whereas (3b) will result in a nasty diagnostic?
When we instantiate the template N::call_func<T>
it will try to pass on the argument of type T
to the unqualified function named func
.
Since the rules of name-lookup says that the current namespace, and the namespace of the arguments involved, are searched for suitable matches in case we are calling a function from a unqualified name, it will only search namespace N if T
is a type declared in namespace N.
Both N::A
and N::B
are declared in namespace N, so the compiler will not search any other scope to find a suitable overload; which is why lookup fails.
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