C++ can't find function out of namespace
Asked Answered
G

2

6

Compiling the following code fails because the second function can't find the first one, even though it's outside namespaces. I couldn't figure out the problem myself, and so far I haven't found any answers on the net.

test.cpp:

#include <bits/stdc++.h>

struct myclass {};

template <typename T, typename U>
std::ostream& operator<< (std::ostream &os, const std::pair<T, U> &p) {
    os << "(" << p.first << ", " << p.second << ")";
    return os;
}

namespace my {
    void operator<< (std::ostream os, myclass m) {
        std::cout << std::pair<int, int>(5, 4); // This is line 13.
    }
}

int main() {
    return 0;
}

Error given by the compiler (g++ test.cpp -O2 -o test.exe):
test.cpp:13:13: error: no match for 'operator<<' (operand types are 'std::ostream {aka std::basic_ostream<char>}' and 'std::pair<int, int>').
And it goes on, giving a long list of suggestions for what operator<< could have meant.

Observation 1: If the two functions differ in name, no error occurs.
Observation 2: If namespace my { } is removed, no error occurs.

Gable answered 22/2, 2019 at 14:31 Comment(3)
related/dupe: #25133883Kurys
By explicitly calling the global operator: ::operator<<(std::cout, std::pair<int, int>(5, 4)); it should work, but that's mighty ugly (and un-chainable). I won't post that as an answer in case there's a better way.Stephanystephen
The better way is to use a using directive before the call (using ::operator<<;). As to why the lookup doesn't end up finding the canidate in global namespace...Scientific
E
5

This is a kind of name hiding; functions/operators can't be overloaded through different scopes.

According to the rule of name lookup,

(emphasis mine)

..., name lookup examines the scopes as described below, until it finds at least one declaration of any kind, at which time the lookup stops and no further scopes are examined.

For this case, the name operator<< is found at the scope of namespace my (i.e. itself), then name lookup stops, the global scope won't be examined, the global operator<< won't be considered for the following overload resolution.

And

Observation 1: If the two functions differ in name, no error occurs.

It's fine because there's no name hiding.

Observation 2: If namespace my { } is removed, no error occurs.

It's fine because the two operator<< is put at the same scope, i.e. the global namespace. Then both operator<< could be found and then considered at overload resolution, the appropriate one will be selected at last.

As the comments suggested, you can apply using to introduce the names in global namespace into namespace my; then both operator<< will be found and then considered in overload resolution.

Epiphytotic answered 22/2, 2019 at 14:47 Comment(0)
S
2

The operator<< defined in namespace my prevents overload resolution to consider your operator<< defined in the global namespace. You can only rely on ADL here or bring the desired overload in the current scope:

namespace my {

std::ostream& operator<<(std::ostream& os, myclass m) {
    // Bringing the operator in the global namespace in the current scope
    using ::operator<<;
    std::cout << std::pair<int, int>(5, 4);
}

}
Scientific answered 22/2, 2019 at 14:47 Comment(2)
or more explicitly: ::operator<<(std::cout, std::pair<int, int>{ 5, 4 });Timid
I prefer the using solution as it still allows ADLScientific

© 2022 - 2024 — McMap. All rights reserved.