Search code examples
c++operator-overloadingname-lookup

C++ Overloaded function name lookup error


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?


Solution

  • 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.