I'm trying to define an equality operator for a type T defined in another namespace, and then use the equality operator on optional<T>. On clang (Apple LLVM 9.1.0), this code:
namespace nsp {
struct Foo {
};
}
bool operator==(const nsp::Foo& a, const nsp::Foo& b);
void foo() {
optional<nsp::Foo> a = none;
optional<nsp::Foo> b = none;
if (a == b)
;
}
Results in an error:
/usr/local/include/boost/optional/detail/optional_relops.hpp:29:34: error: invalid operands to binary expression ('const nsp::Foo' and 'const nsp::Foo')
{ return bool(x) && bool(y) ? *x == *y : bool(x) == bool(y); }
~~ ^ ~~
MWE.cpp:40:19: note: in instantiation of function template specialization 'boost::operator==<what3words::engine::nsp::Foo>' requested here
if (a == b)
^
/usr/local/include/boost/optional/detail/optional_relops.hpp:28:6: note: candidate template ignored: could not match 'optional<type-parameter-0-0>' against 'const nsp::Foo'
bool operator == ( optional<T> const& x, optional<T> const& y )
What's happening? My guess is that it's something to do with the Koenig lookup rules...
Immediate fix
Do this:
namespace nsp {
bool operator==(const Foo& a, const Foo& b);
}
to fix your problem.
If you have control over Foo, you can instead do:
namespace nsp {
struct Foo {
friend bool operator==(const Foo& a, const Foo& b) {
return true;
}
};
}
which is optimal if Foo is a template class.
What went wrong with your solution
What is going on here is that optional is in std (or boost or whatever) and in that namespace it tries to do a nsp::Foo == nsp::Foo call.
There is a == that doesn't apply in the ::std namespace, so it won't look in ::; once it finds any == it stops looking, even if the arguments are completely unrelated. It also looks for == in the namespaces associated with the arguments -- in this case ::nsp. But it never looks in :: here either.
When adding operators to a type, always define the operator in the namespace of the type.
Namespaces can be reopened. So if you don't have control over the header file, you can create a new header file with the == in it. This == has to be visible at every point where optional<nsp::Foo>::operator== is invoked or your program is ill formed due to ODR violations (and, in this case, also generates a compiler error, which is useful to avoid heizenbugs).
The long version
When you invoke an operator (or a function), lookup follows a few simple steps.
First it looks around locally (in the local namespace). If it finds anything there, this search stops. (This includes using ns::identifier; names injected into the namespace, but usually not using namespace foo;). This "stop" occurs even if the function or operator won't work for the types in question; any == for any types whatsoever in a namespace stops this search.
If that fails to find a match, it starts looking in enclosing namespaces until it finds the function/operator, or reaches the root namespace. If there are using namespace foo; declarations, the functions/operators in those namespaces are considered to be in the "common parent" namespace of both the using namespace location and the namespace being imported. (So using namespace std; in namespace foo makes it seem like std is in ::, not in foo).
The result generates a collection of candidates for overload resolution.
Next, ADL (argument dependent lookup) is done. The associated namespaces of all function/operator arguments are examined. In addition, the associated namespaces of all the type arguments of the templates are also examined (recursively).
Operators/functions that match the name are collected. For ADL, parent namespaces are not examined.
These two collections of operators/functions are your candidates for overload resolution.
In your case, the namespace where == is called is boost. boost has plenty of == operators (even if they don't apply), so all of the == in boost are candidates.
Next, we examine the namespace of the arguments -- nsp::Foo in this case. We look in nsp and see no ==.
Then we run overload resolution on those. No candidates work, and you get a compiler error.
Now, when you move your user-defined == into namespace nsp, it is then added to the set of == found in the ADL step. And as it matches, it is called.
Overload resolution (what it does with the candidates) is its own complex subject. The short form is that it tries to find the overload that involves the least amount of conversion; and if two cases match exactly as well as each other, it prefers non-template over template and non-variardic over variardic.
There is a lot of detail in "least amount of conversion" and "exactly" that can mislead programmers. The most common is is that converting a Foo lvalue to Foo const& is a small amount of conversion, convering it to template<class T> T&& or T& is no conversion.
Indeed, you should enable ADL by declaring the operator== implementation inside an associated namespace for Foo. This fixes that:
#include <boost/optional.hpp>
namespace nsp {
struct Foo { };
bool operator==(const Foo&, const Foo&) { return false; }
}
int main() {
boost::optional<nsp::Foo> a;
boost::optional<nsp::Foo> b;
return (a == b)? 0 : 1;
}
Related
I have this code:
#include <iostream>
namespace FooStuff
{
struct Foo { };
}
decltype(std::cout)& operator << (decltype(std::cout)& left, const FooStuff::Foo& right)
{
return left;
}
void test1()
{
// This works fine
std::cout << FooStuff::Foo();
}
As far as I can tell, this is the best operator << that one could possibly write down to match the call in test1, and it works as you would expect.
Now add this code below the code above:
namespace BarStuff
{
struct Bar { };
// Danger zone
void test2()
{
// This works too, for now
std::cout << FooStuff::Foo();
}
}
The call in test2 works too, as you would expect.
But if I insert the following operator right below the "Danger zone" comment, everything breaks:
Bar operator << (Bar left, Bar right)
{
return left;
}
Now the call in test2 won't compile because the compiler chooses the completely inappropriate overload that takes a bunch of Bars, even though the operator from the first snippet should be a perfect match:
main.cpp:33: error: invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char>') and 'FooStuff::Foo')
(A ton of unrelated overloads omitted for brevity)
main.cpp:25: candidate function not viable: no known conversion from 'std::ostream' (aka 'basic_ostream<char>') to 'BarStuff::Bar' for 1st argument
What the poop is going on? Why does the compiler choose that overload instead of the one I want it to choose?
And why does the error disappear, if Foo is moved outside FooStuff?
When you write std::cout << FooStuff::Foo(); name lookup is done to determine the candidates for << to use in overload resolution.
For overload resolution of operators there are two parts to this lookup: unqualified name lookup of operator<< and argument-dependent lookup of operator<<.
For unqualified name lookup, as is always the case, lookup traverses from inner to outer scope and stops as soon as a match is found. So if you put an overload at // Danger zone, the one outside BarStuff will be hidden.
For argument-dependent name lookup, all overloads in the class types of the operands and their immediately enclosing namespace will be considered. In your case that means that overloads inside struct Foo and namespace FooStuff will be found as well, no matter where the std::cout << FooStuff::Foo(); is placed.
For the above reasons, operator overloads should be placed in the namespace containing the class for which they overload the operator. This assures that the overload is always found via argument-dependent lookup.
Why doesn't the following code compile?
#include <iostream>
namespace X
{
inline std::wostream & operator<<(std::wostream & stm, int a)
{
stm << L"int";
return stm;
}
}
namespace Y
{
class A
{
};
}
inline std::wostream & operator<<(std::wostream & stream, const Y::A & msg)
{
stream << L"A";
return stream;
}
namespace X
{
void f()
{
Y::A a;
std::wcout << a;
}
}
and why removing operator << in namespace X, makes the code compile? Try to comment it out, for example:
namespace X
{
//inline std::wostream & operator<<(std::wostream & stm, int a)
//{
// stm << L"int";
// return stm;
//}
}
what is the dependency between these operators?
see live example.
EDIT1:
The only guess I have is that the operator declared in the same namespace where it is used hides the operators from other namespaces somehow, but I never heard about that before...
EDIT2:
Actually in my project the second operator is in namespace Z (but not global):
...
namespace Z
{
inline std::wostream & operator << (std::wostream & stream, const Y::A & msg)
{
stream << L"A";
return stream;
}
}
namespace X
{
void f()
{
using namespace Z;
Y::A a;
std::wcout << a;
}
}
that results in the same compiler error.
This behavior is actually expected in C++, in order to avoid unexpected behaviors introduced by different overloads in different namespaces.
This is called name hiding.
You can read a really good answer on the subject here: https://stackoverflow.com/a/1629074/182676
So overloads in different namespaces will hide each other.
You can fix this by making the correct overload visible to the compiler with using:
Y::A a;
using Z::operator<<;
std::wcout << a;
NOTE: lookup of overloaded operators is substantially different to class member function lookup as suggested by other comments/answers. See this answer for an introduction to the name lookup rules for overloaded operators.
In your first example, std::wcout << a inside X::f(), name lookup finds:
Qualified lookup for member functions of the left operand: std::wostream has a member function operator<<.
Unqualified lookup: the current scope is in namespace X, so X::operator<< is found, and we stop here. This stage only goes up to check parent namespaces if the name is not found.
Argument-dependent lookup: the arguments are std::wcout and Y::a, so the ADL namespaces are std and Y.
So the overload set consists of:
(QL) All member functions std::wostream::operator<<.
(UL) All free functions X::operator<<.
(ADL) All free functions std::operator<<
(ADL) All free functions Y::operator<< (or would, if there were any).
and nothing else.
None of those find a match for argument type Y::A so compilation fails.
When you remove X::operator<<, then the unqualified lookup step finds nothing in X so it looks in the parent namespace, recursively. Then ::operator<< is found and that function goes into the overload set, and compilation succeeds.
To avoid this problem, the usual procedure is to put free overloaded operators of user-defined types into the same namespace as the type was defined, so in this case you would do:
namespace Y
{
inline std::wostream & operator<<(std::wostream & stream, const A & msg) { .... }
}
and then the ADL step would find this function even though the unqualified lookup step also finds X::operator<<.
In your second example the exact meaning of using namespace Z; is:
During unqualified name lookup, the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace.
The nearest enclosing namespace containing both X and Z is the global namespace, so the names behave as if in the global namespace for the unqualified lookup phase.
Therefore the process is not substantially different to my analysis for your first case, and only X::operator<< is found by unqualified lookup. Again, this would be fixed by including the desired overload in Y so that it is found by ADL.
The following code fails to compile
namespace A {
using C = std::vector<std::string>;
std::ostream& operator << (std::ostream& lhs, const C& rhs) {
lhs << 5;
return lhs;
}
}
int main()
{
A::C f;
std::cout << f;
return 0;
}
with the error
Error C2679 binary '<<': no operator found which takes a right-hand operand of type 'A::C' (or there is no acceptable conversion)
Obviously it cant find the << operator presumably due to considering C to be a class from the std namespace. Is there some way to ensure the compiler finds this operator or otherwise work around the problem?
A::C is just a type alias, and aliases are transparent. They don't "remember" where they came from. When we do argument-dependent lookup and figure out what the associated namespaces are, we only consider the associated namespaces of the types - not the alias that got us there. You can't just add associated namespaces to existing types. The specific associated namespace of f (which is of type std::vector<std::string>) is std, which doesn't have an operator<< associated with it. Since there's no operator<< found using ordinary lookup, nor is there one found using ADL, the call fails.
Now, I said you can't just add associated namespaces to existing types. But you can of course just create new types:
namespace A {
struct C : std::vector<std::string> { };
}
or:
namespace A {
// template parameters are also considered for associated namespaces
struct S : std::string { };
using C = std::vector<S>;
}
I came across a strange situation today, where declaring a deleted operator with certain arguments changed the behaviour of seemingly unrelated code.
I reduced it to the following. Start with this:
namespace N
{
enum E { A, B };
struct C
{
C(E);
private:
C(int);
};
}
N::E operator|(N::E, N::E);
namespace N
{
void Waldo()
{
C(A | B);
}
}
Notice that C has two constructors, a public one and a private one. This code compiles, indicating that the public overload is being chosen, so the expression A | B has type E. In turn this means that the operator|(N::E, N::E) has been matched (otherwise A and B would undergo implicit conversion to integers, the type of A | B would be int, and the private constructor would be matched.
So far so good. Now I define a new enumeration type F, and a deleted operator| that involves F:
namespace N
{
enum E { A, B };
struct C
{
C(E);
private:
C(int);
};
}
N::E operator|(N::E, N::E);
namespace N
{
enum F {};
int operator|(F, int) = delete;
void Waldo()
{
C(A | B);
}
}
Now the code doesn't compile, saying that C(int) is private. This indicates that now A | B has type int, which means operator|(N::E, N::E) is no longer being matched.
Why did the addition of the deleted operator|(F, int) stop operator|(N::E, N::E) from being matched?
First off, note that being declared as deleted is irrelevant, since deleted functions still take part in overload resolution.
Now, on to overload resolution. Cf. 13.3.1.2/3:
three sets of candidate functions, designated member candidates, nonmember
candidates and built-in candidates, are constructed
(There are no member candidates, since E is not a class type.) We know from that the operator overload is found by unqualified lookup. So when we consult 3.4.1 ("Unqualified lookup"), we find that
name lookup ends as soon as a declaration is found for the name.
Since you introduce the second operator overload within the namespace N, it is found first, and name lookup stops. At this point, the overload set consists of your int N::operator|(N::F, int) and the built-in operators. Continuing in 13.3.1.2/6:
The set of candidate functions for overload resolution is the union of the member candidates, the non-member candidates, and the built-in candidates.
Only the builtin is viable (since you cannot convert E to F implicitly), and thus it is chosen.
The solution to your problem is simple.
Put your operator| in the same namespace as the type. Now, ADL (argument dependent lookup) kicks in, and it is found even if there is the unrelated operator| also visible.
Live example. Note that N::operator| is found despite the | being used in namespace Z.
The proper place to overload free operators for a type is the namespace that the type lives in, not the global namespace.
The following code is giving me a compilation error. Can anyone please tell me why?
class mytype {
public:
int value;
mytype(int a) {
value = a;
}
friend ostream& operator<<(ostream& stream, const mytype& a) {
stream << a.value;//works
return stream;
}
friend ostringstream& operator<<(ostringstream& stream, const mytype& a) {
stream << (a.value);//compilation error
return stream;
}
};
Error:
error C2027: use of undefined type
'std::basic_ostringstream<_Elem,_Traits,_Alloc>'
Upon fixing that:
error C2666: 'operator <<' : 18 overloads have similar conversions
Final fix:
Declare constructor as explicit. Works on MSVC then.
I wonder why.
error C2027: use of undefined type 'std::basic_ostringstream<_Elem,_Traits,_Alloc>'
You need #include <sstream> to get the [i/o]stringstream classes.
About the other errors
The problem with an overload of the form
ostringstream& operator<<(ostringstream& stream, const mytype& a)
is that it matches an ostringstream exactly. Why is it a bad thing to have a more exact overload? From the standard, §13.3.3/1-2:
a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2) …
If there is exactly one viable function that is a better function than all other viable functions, then it is the
one selected by overload resolution; otherwise the call is ill-formed
Therefore if operator<<( ostream &, int ) and operator<<( ostringstream &, mytype const & ) are both candidates for stream << value, the former matches int exactly with no conversion but the latter matches ostringstream exactly. Therefore neither can be "not worse" for all arguments, and neither candidate may be chosen.
But the code is valid because of a loophole. Your overload is not a candidate at all, except when you actually use your type in the function call. When you declare/define a friend inside a class block, it does not introduce it to any namespace scope; it merely associates it with the class scope, which allows it to be found if that class type describes one of the arguments being passed.
The standard has this to say about friend declarations, although it's in another section (14.6.5):
Friend declarations do not introduce new names into any scope…
So, MSVC tried to be nice and proactively introduced your friend to its enclosing namespace. Strike against MSVC.
However, when I attempted to add a declaration equivalent to what MSVC did "for free," Comeau and GCC nicely resolved the resulting overloading conflict — strike against them. Or is it? As it turns out, the overloaded call occurs earlier in the file than my recommended declaration. If I move the declaration before class mytype { (which requires forward-declaring mytype), then both properly complain about the ambiguity.
Using an overload before it is declared in namespace scope appears to be well and good according to §3.3-3.4 of the Standard. So actually GCC and Comeau were both in the right. The point of declaration is right after the name of the declared object. (And last I checked, self-referential function declarations can still crash GCC.) ADL invokes unqualified lookup into the enclosing namespace at a point immediately before the enclosing class. (3.4.1/8 final bullet, 3.4.1/9, 3.4.2/2a.) If the friend hasn't been declared before the class, it's legitimately not a candidate. (7.3.1.2/3) Isn't C++ a beautiful language?
How keep the simplified example on GCC, but break subsequent code.
friend ostringstream& operator<<(ostringstream& stream, const mytype& a) {
stream << (a.value);//compilation error
return stream;
}
};
ostringstream& operator<<(ostringstream& stream, const mytype& a); // <- here
Following this declaration, it will be impossible to write an int into an ostringstream.
How to break everything uniformly, with simpler declaration semantics.
class mytype; // <- here
// and here:
inline ostringstream& operator<<(ostringstream& stream, const mytype& a);
class mytype {
public:
Following this declaration, it will be impossible to write an int into an ostringstream… including the friend declarations inside class mytype {}.
Actual solution.
The stream classes are supposed to be indistinguishable. If you really want to determine whether a given stream feeds a string in memory (and you shouldn't), it's best to look at its internal streambuf object, returned by rdbuf(), which actually performs the I/O gruntwork. Even a generic ostream object can have ostringstream functionality if given a stringbuf.
if ( typeid( stream.rdbuf() ) == typeid( stringbuf * ) ) {
// this is effectively a stringstream
} else {
// not a stringstream
}
There is an overload ambiguity in the call to operator<< in the ostringstream overload.
stream << a.value;
There are a number of operator<< overloads that are members of the ostream class, which is a base class of ostringstream. One of these is declared as:
ostream& ostream::operator<<(int);
This overload is an exact match for the right-hand side (a.value is an int) but requires a derived-to-base conversion on the left-hand side (stream is an ostringstream, which is derived from ostream).
However, there is also your ostringstream overload:
ostringstream& operator<<(ostringstream&, const mytype&);
This overload is an exact match for the left-hand side (stream is an ostringstream), and a user-defined converting constructor (your mytype(int) constructor) can be used to convert a.value (an int) to a mytype.
Since one overload matches the first argument better and the other overload matches the second argument better, there is an ambiguity. You can fix this either by:
Explicitly converting the left-hand side to an ostream (using (ostream&)stream = a.value;), or
Remove the user-defined conversion by making the constructor explicit.