I encountered a strange error regarding name lookup in C++.
The error can be recreated using the following minimal example:
#include <vector>
#include <iostream>
std::ostream& operator<<(std::ostream& out, const std::vector<int>& a) {
for (size_t i = 0; i < a.size(); i++) {
out << a[i] << std::endl;
}
return out;
}
namespace Test {
struct A {
// Label 1
friend std::ostream& operator<<(std::ostream& out, const A&) {
return out << "A" << std::endl;
}
};
struct B {
void printVector() noexcept {
std::vector<int> v{1, 2, 3};
std::cout << v << std::endl; // The error occurs in this line
}
// Label 2
friend std::ostream& operator<<(std::ostream& out, const B&) {
return out << "B" << std::endl;
}
};
}
int main() {
Test::B().printVector();
}
Compiling this will result in the following error message:
cannot bind 'std::ostream {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&'
You can test this for yourself here: http://cpp.sh/5oya
The strange part is, that the code compiles and runs fine if you remove either one of the functions labeled with // Label 1
respectively // Label 2
.
My question now is: What is going on here? How can it be fixed?
Other workaround will be to overload operator << inside the namespace std, instead of in the global namespace.(The lookup will find it at namespace scope)
namespace std
{
std::ostream& operator<<(std::ostream& out, const std::vector<int>& a) {
for (size_t i = 0; i < a.size(); i++) {
out << a[i] << std::endl;
}
return out;
}
}
Try Here
[EDITED]
Another workaround, and for puritan people that don't want to pollute global namespace, or the std
, is to
...have the insertion operators in the same namespace as the class upon which it operates.
...as discussed here.
namespace Test
{
std::ostream& operator<<(std::ostream& out, const std::vector<int>& a) {
for (size_t i = 0; i < a.size(); i++) {
out << a[i] << std::endl;
}
return out;
}
}
Working code here.