Why Is Operator""S Hidden in a Namespace

Why is operators hidden in a namespace?

There are actually two reasons why literals are put into namespaces:

  1. It is considered undesirable that users would use using namespace std; just to get hold of the corresponding literals. Having the literals declared in namespaces specific to these doesn't cause the problem.
  2. Depending on the domain it may be desirable to use s as the suffix for something else. There is already another suffix s to mean seconds but they don't really conflict.

In the video of STL's CppCon 2014 talk (posted by remyable in a comment) Stephan T. Lavavej explains the overall design of the literals in C++14 and its pretty clear that they are not supposed to be in the global namespace! Instead, the literal suffixes in the standard library live in a hierarchy of inline namespaces giving users fine-grained control over the literals being made available. For example, the literal suffix for strings is declared like this (21.3 [string.classes] paragraph 1):

namespace std {
inline namespace literals {
inline namespace string_literals {
string operator"" s(char const* str, size_t len);
}
}
}

This hierarchy of inline namespaces makes it possible for users to get the appropriate choice of literal suffixes:

  • using namespace std; - you get everything in the standard C++ library, including the literal suffixes, without any qualification.
  • using namespace std::literals; - you get all literal suffixes defined in the standard C++ library.
  • using namespace std::string_literals; - you get all literal suffixes applicable to strings.
  • using namespace std::literals::string_literals; - yes, you can do that but you really shouldn't: that's equivalent to using namespace std::string_literals;.

Clearly, the committee wouldn't have gone to that much effort if it had considered the idea viable to just pollute the global namespace with literal suffixes, although they can't even conflict with any user literal suffixes.

In what cases do global operators get hidden in C++?

You're probably forgetting how name lookup works. When you're using operator<<, it's almost always as a << b, not ::parent::namespaceA::operator<<(a,b). That unqualified use means that it's looked up via Argument Dependent Lookup (aka Koenig Lookup).

So, operators from the global namespace will be hidden by operators from the argument's namespaces.

Operator in namespace scope hiding another in global scope

No that is not a bug. There are three parallel sets of operators considered. Members, non-member operators, and builtins.

The non-member ones are looked up by normal unqualified+ADL lookup, ignoring all class member functions. Hence the global operator is hidden by a lexical more closer one (and an intervening member function wouldn't have hidden other non-members).

Note that overload resolution takes place after name lookup1; in your case the name operator++ was found, but no appropriate overload.

If Bar had been declared globally, and/or the other operator in namespace asdf, ADL (in the former case) or ordinary unqualified lookup (in the latter case) would have dragged the operator in.


1: Overload resolution (...) takes place after name lookup has succeeded. (C++ Standard)

Accessing hidden operator from parent namespace in C++

You can do it using explicit function call syntax. For your case, the call should be os.operator<<(b ? 10 : -10), because the corresponding operator<< is a member function.

However, with your operator<<, you will no longer be able to use expressions such as std::cout << true in namespace Foo, because this will trigger an ambiguity between your Foo::operator<<(std::ostream&, bool) and std::ostream's member function std::ostream::operator<<(bool): both accept an lvalue of type std::ostream as its left operand, and both accept a value of type bool as its right operand, neither is better than the other.

ostream operator in Namespace hides other ostream::operator

As stated here, this is an example of name hiding. By defining operator<< in namespace MyNamespace all the definitions from the higher namespaces (like global) are hidden.

Note that as stated here:

[...]this feature doesn't interfere with Koenig lookup [...], so IO operators from std:: will still be found.

(details about Koenig lookup)

The solution is to refer to the overload in the other namespace with the using directive, as described here and here. This was mentioned in the comments by Michael Nastenko.

Thus using ::operator<<;, with :: referring to the global namespace.

Thus the code will become:

#include <string>
#include <iostream>

typedef std::pair<std::string, std::string> StringPair;

std::ostream& operator<<(std::ostream& os, const StringPair &pair) {
os << pair.first << '.' << pair.second;
return os;
}

namespace MyNamespace {
class MyClass {};
using ::operator<<;
std::ostream& operator<< (std::ostream&, const MyClass &);
void xxx();
}

void MyNamespace::xxx() {
StringPair pair("1","2");
std::cout<<pair<<std::endl;
}

int main() {
MyNamespace::xxx();
return 0;
}

example on Coliru

Unable to find operator defined in the global namespace

For consistency, operators are considered to be just function names, and a function hides any in an enclosing scope (even if the parameter types differ). (The latter rule is presumably intended to prevent unexpected overload resolution interaction between functions declared in different scopes).

Your ::operator==(const X::A&,const X::A&) is hidden (within Y) by Y::operator==(const B&,const B&), and it's not considered to be part of the interface of X::A (because it's not in the immediate namespace X), so you can't find it.

If you're willing to change the spelling of the comparison, you can use the ugly, explicit ::operator==(b.an_a(), A::aaa) instead of adding workaround.

Cannot find () and () declared in unnamed namespace

When using an operator like << (or calling a function with unqualified name) there are two ways that matching names (i.e. here operator<< overloads) can be found as candidates.

The first way is by simple unqualified name lookup which traverses the scopes from inner to outer until the name is found, and then stopping.

The second is via argument-dependent lookup (ADL) by looking for it in the namespaces of classes whose type appears as part of the arguments of the function call, or here the operands of the << operator.

Usually both lookups are performed from the point where the call appears and declarations which are only introduced after that point in the translation unit are not considered.

However if the call appears in a template, as is the case here for out << P{}; there are two (or maybe even more) points from which lookup could be performed: The point where the template containing the call is defined and the point where a specific template specialization is instantiated.

In your code the definition of the template is before the declaration of the operator<< overload in the unnamed namespace and the instantiation is after it (possible points are immediately before the definition of save_items or at the end of the translation unit).

So, one possibility would be that name lookup is done either only from one of the two points or equally from both. But the actual rules are that the simple unqualified name lookup is performed only from the point of definition and the argument-dependent lookup is performed from the point of instantiation.

So your overload could only be found via ADL, but ADL requires the function to be in the same namespace as the type(s) of the argument(s), which is here not the case, since A is not part of the unnamed namespace.

The rules are chosen this way with the conventional understanding that you will put operator overloads specific to a given class in the same namespace and header as the class itself.

Therefore GCC 12 is correct and the previous versions were wrong to accept the code.

Prior to version 12 GCC had a bug which also considered the unqualified name lookup from the point of instantiation instead of the point of definition when looking up operator overloads for operator uses. See bug 51577.

The unnamed namespace is a red herring, by the way. If you put the operator<< overload directly into the global namespace or any other namespace that isn't ns_f, nothing about the above changes.

Overloading an operator in a namespace and a sub-namespace in C++17 is ambiguous

I expected to have to 'name' the operator before I can do that, as in using test::operator<<, but I don't have to.

This is because of Argument Dependent Lookup (ADL).

If you pull the operator from the debug namespace into the current scope via the using, this does not "overwrite" the existing operator, they are both avaliable, hence the ambiguity.

There are many ways to deal with it, one possibility is to use a different type for the debug output:

namespace test {
namespace debug {
struct debug_A {
const A& data;
debug_out(const A& a) : a(a) {}
};

std::ostream& operator<< (std::ostream& os, const debug_A& d) {
auto& a = d.data;
std::string info = "\n\tDebug\n"
"\t\tLine: " + std::to_string(__LINE__) + "\n"
"\t\tFile: " __FILE__ "\n"
"\t\tDate: " __DATE__ "\n"
"\t\tTime: " __TIME__ "\n"
"\t\tVersion: " + std::to_string(__cplusplus) + "\n";
return a.toStream(os) << info;
}
}
}

Now you can call it via

std::cout << test::debug::debug_A{ a } << '\n';

Why should we use :: operator on global functions / objects?

To avoid accidental namespace clashing. For example if you current namespace would have glGenBuffers which does something different from the "good" glGenBuffers with :: you can specify to call the glGenBuffers which is in the global namespace.



Related Topics



Leave a reply



Submit