This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
how to provide a swap function for my class?
Every time I think I understand it, I see something that really confuses me.
If you want to provide an implementation of swap for your own class, what do you do?
The list of possibilities is:
Define one inside the std namespace (taking two arguments), which calls #3 below
(some say this is correct; some people say illegal)
Define a static method inside the class, taking two arguments to swap
(makes more sense to me than #4, but I don't get why no one does this), which calls any base-class swaps as necessary
Define an instance method inside the class, taking one other argument to swap with, which calls any base-class swaps as necessary
Define an instance method inside the class, taking two other arguments to swap, also here, which calls any base-class swaps as necessary
Define one in your own namespace (taking two arguments), which calls #3
Something else
My own understanding is that I need #5 and #3, where the caller would then be calling swap likeusing std::swap; swap(a, b);,but the fact that no one seems to suggest that combination is really confusing me. And I really don't understand #4 at all, because everyone seems to be using an instance member when in fact the operation is static. I can't tell if my understanding is wrong or a bunch of the answers I see when looking this up.
What's the correct way?
A common pattern I have seen is providing 3 and 5, as per your own understanding.
adds an specialization to the std:: namespace, which is allowed, but might not be possible in all cases (if your type is a template itself).
offers no advantage at all, and forces qualifying with the type when used outside of one of the members, which means that for implementing swap on other types that hold your type as a member, they will need to qualify the call (void swap( other& l, other& r ) { T::swap( l.t, r.t ); })
does not need friendship, allows for use with rvalues (even in C++03) and is idiomatic in some cases std::vector<int>().swap( v ); to clear the contents of the vector.
What? You misunderstood the code! That is not declaring a member taking two arguments, but rather a free function taking the two arguments, and defines the function inline. This is equivalent to 5 (without forwarding to 3, but rather implementing everything in the free function).
Free function in the same namespace allows for ADL to find it, and enables other code to use the common pattern of void swap( other& l, other& r ) { using std::swap; swap( l.t, r.t ); } without having to know whether the type of other::t has an specific swap overload or the one in std:: needs to be used. Forwarding to 3 allows you to provide a single (real) implementation that can be used through ADL and also on temporary objects.
The C++11 standard says an object t is swappable with an object u if swap(t, u) and swap(u, t) are valid expressions that select non-member functions called swap from an overload set that includes the two std::swap templates in <utility> and any overloads found by Argument Dependent Lookup, such that the values of t and u are exchanged.
The conventional way to swap two objects under the conditions described above is:
using std::swap;
swap(t, u);
Here name lookup will consider std::swap and any overloads of swap found by ADL, then overload resolution will pick the best match.
Considering each of your implementations:
It is not legal to add overloads to namespace std, and the rule above doesn't require them to be found anyway. It is legal to specialize the standard std::swap function templates if the specialization is for a user-defined type (i.e. not a standard library type or fundamental type.) If you specialize std::swap it's valid, otherwise not, and you can't partially-specialize function templates.
A static member function will not be found by swap(t, u) because it needs to be qualified e.g. Foo::swap(t, u)
A unary swap member function cannot be called as swap(t, u), it has to be called as t.swap(u)
The links you give show a non-member friend function, which can be found by ADL, so can be used to make types swappable.
Such a function can be found by ADL.
So 2 and 3 do not make a type swappable. 4 does, and 5 does. 1 might do, if done correctly, but cannot be used for swapping class templates.
3 is not required, but can be used to help implement either 4 or 5, because a member function will have access to the type's internal details so can swap private members. For 4 the function is a friend, so has access already. So by a process of elimination you need either 4 or 3 and 5. It is generally considered better to provide a member swap (3) and then provide a non-member function in the same namespace (5) which calls the member swap.
As you say, you need #5 (a function in the same namespace as your type) to support the idiomatic using std::swap; swap(a,b);. That way, your overload will be selected by argument-dependent lookup in preference to std::swap.
If your implementation of swap needs to access the type's privates, then you will either need to call a member function like #3, or declare the non-member function a friend. This is what your examples in #4 do: a friend function can be declared and defined inside the class, but that does not make it a member; it is still scoped within the surrounding namespace.
So this:
class thing {
friend void swap(thing & a, thing & b) {/*whatever*/}
};
is equivalent to this (more or less - see comments):
class thing {
friend void swap(thing & a, thing & b);
};
inline void swap(thing & a, thing & b) {/*whatever*/}
#1 (specialising std::swap for your type) is allowed, but some would regard it as cleaner to keep everything in your own namespace.
Neither #2 nor #3 would allow an unqualified swap(a,b) to find your implementation.
Related
What is the copy-and-swap idiom?
in this question, in the top answer, within the section where the swap public friend overload is implemented, the implementation makes use of this:
friend void swap(dumb_array& first, dumb_array& second){
//the line of code below
using std::swap;
//then it calls the std::swap function on data members of the dumb_array`s
}
My question is the following: what is the using std::swap used for here (the answer mentions something related to enabling ADL); what use case of "using" is specifically being invoked here and what are the effects of adding that line of code and the effects of not adding it on the code?
The using statement makes this line work:
swap(first, second);
Notice that we can omit std:: in front of swap.
The important thing there is that std::swap(...) is a qualified lookup, but swap(...) is an unqualified lookup. The main difference is that qualified lookup is calling a function in a specific namespace or scope (the one specified), whereas unqualified lookup is a bit more flexible since it will look into parent scope of the current context and also the global namespace. In addition, unqualified lookup will also look into the scope of the type of the arguments. It's a nice tool, but also dangerous since it can call function from unexpected places.
ADL will only work with unqualified lookup, since it has to search for other namespaces and scopes.
The using std::swap also ensure that if no function is found through ADL, it will call std::swap by default.
This idiom allow for user defined swap functions:
struct MyType {
// Function found only through ADL
friend void swap(MyType& l, MyType& r) {
// ...
}
};
Since C++20, the concept of customization point is introduced in [namespace.std]/7:
Other than in namespace std or in a namespace within namespace std, a program may provide an overload for any library function template designated as a customization point, provided that (a) the overload's declaration depends on at least one user-defined type and (b) the overload meets the standard library requirements for the customization point. [ Note: This permits a (qualified or unqualified) call to the customization point to invoke the most appropriate overload for the given arguments. — end note ]
Does the note part (note the emphasized word "qualified") mean that std::f will automatically invoke the most appropriate overload for f if std::f is a customization point?
A real example is std::swap, which is a designated customization point. Does this mean since C++20, we can write std::swap(a, b) directly instead of using std::swap; swap(a, b);?
A real example is std::swap, which is a designated customization point. Does this mean since C++20, we can write std::swap(a, b) directly instead of using std::swap; swap(a, b);?
No. std::swap itself did not gain any powers. It's still just a function template, so if you call it directly, you're... calling it directly. No ADL or anything.
The point of this is to say how customization points should be opted into. That is, you write:
namespace N { // not std
void swap(Foo&, Foo&);
}
Not:
namespace std {
void swap(N::Foo&, N::Foo&);
}
Nor:
namespace std {
template <>
void swap(N::Foo&, N::Foo&);
}
However, C++20 does introduce a lot of new things called customization point objects which you can use directly do this kind of thing. The CPO for swap is spelled std::ranges::swap (and likewise there are CPOs for all the useful ranges things... ranges::begin, ranges::end, etc.).
I've been combing through the internet to find an answer, but I couldn't find any. The only reasons given seems to be relevant for comparing with objects of different type (e.g. MyClass == int). But the most common use case is comparing a class instance to another instance of the same class, not to any unrelated type.
In other words, I do understand the problems with:
struct A {
bool operator==(int b);
};
But I cannot find any good reason to not use member function in the most obvious use-case:
struct A {
bool operator==(const A&);
};
The most canonical duplicate What are the basic rules and idioms for operator overloading? says "overload binary operators as non-member" as rule of a thumb.
Operator overloading : member function vs. non-member function? gives example mentioned above - if you were to use this operator with instance of another class/primitive type...
CppCoreGuidelines has a vague explanation "If you use member functions, you need two", which I assume applies to comparing with object of different type.
Why should operator< be non-member function? mentions that "non-member functions play better with implicit conversion", but it seems again the case of left-hand operand not being the instance of the class.
On the other hand, member overload seems to have a couple positive sides:
No need to befriend the function or to provide getters for members
It is always available to class users (although this might be the downside also)
No problems with lookup (which seems to be common in our GoogleTests for some reason)
Is overloading operator== as non-member function just a convention to keep it the same with possible overloads in other classes? Or are there any other reasons to make it non-member?
Well, in your question, you did forget to const qualify the member function, and it would be harder to write bool operator==(A&, const A&); by accident.
If you had an implicit constructor, a class with implicit conversion to A or base class with an operator== with higher priority, the member function wouldn't work if it was on the left, but would if it was on the right. Although most of the time implicit conversions are a bad idea, inheritance could reasonably lead to a problem.
struct A {
A(int); // Implicit constructor
A();
bool operator==(const A&) const;
};
struct B : A {
bool operator==(const B&) const;
};
void test() {
A a;
B b;
// 1 == a; // Doesn't work
a == 1;
// b == a; // Doesn't work; Picks `B::operator==(const B&) const;`
a == b; // Picks `A::operator==(const A&) const`, converting `b` to an `A&`.
// Equality is no longer symmetric as expected
}
In the future, with the C++20 operator<=>, you will most likely always implement this as a member function (namely as auto operator<=>(const T&) const = default;), so we know that this guideline may change.
The arguments for using non-member operator overload for symmetric operations are based on style and consistency. They are not very strong arguments 1. Non-member overloads are typically preferred because a weak argument is still a little bit better than no argument at all.
Your arguments for member operator overload don't seem to be any stronger. Consider following:
No need to befriend the function
On the other hand, if you use a non-member overload, then you don't have need to declare a member function. Is befriending the non-member somehow worse?
or to provide getters for members
There is no need for that if you befriend the overload.
It is always available to class users (although this might be the downside also)
It is unclear how this differs from the non-member overloads. Are they also not always available to the class users?
No problems with lookup (which seems to be common in our GoogleTests for some reason)
Are there lookup problems with non-member overloads? Could you demonstrate the problem with an example, and show how the problem is solved by using a member overload instead?
If it does solve the problem, then you can of course use that. Just because some guidelines recommend that you prefer one alternative as a rule of thumb, doesn't mean that is the only alternative to be used in all use cases.
1 Although, see answer https://stackoverflow.com/a/57927564/2079303 which is arguably stronger than just stylistic.
I've been combing through the internet to find an answer, but I couldn't find any. The only reasons given seems to be relevant for comparing with objects of different type (e.g. MyClass == int). But the most common use case is comparing a class instance to another instance of the same class, not to any unrelated type.
In other words, I do understand the problems with:
struct A {
bool operator==(int b);
};
But I cannot find any good reason to not use member function in the most obvious use-case:
struct A {
bool operator==(const A&);
};
The most canonical duplicate What are the basic rules and idioms for operator overloading? says "overload binary operators as non-member" as rule of a thumb.
Operator overloading : member function vs. non-member function? gives example mentioned above - if you were to use this operator with instance of another class/primitive type...
CppCoreGuidelines has a vague explanation "If you use member functions, you need two", which I assume applies to comparing with object of different type.
Why should operator< be non-member function? mentions that "non-member functions play better with implicit conversion", but it seems again the case of left-hand operand not being the instance of the class.
On the other hand, member overload seems to have a couple positive sides:
No need to befriend the function or to provide getters for members
It is always available to class users (although this might be the downside also)
No problems with lookup (which seems to be common in our GoogleTests for some reason)
Is overloading operator== as non-member function just a convention to keep it the same with possible overloads in other classes? Or are there any other reasons to make it non-member?
Well, in your question, you did forget to const qualify the member function, and it would be harder to write bool operator==(A&, const A&); by accident.
If you had an implicit constructor, a class with implicit conversion to A or base class with an operator== with higher priority, the member function wouldn't work if it was on the left, but would if it was on the right. Although most of the time implicit conversions are a bad idea, inheritance could reasonably lead to a problem.
struct A {
A(int); // Implicit constructor
A();
bool operator==(const A&) const;
};
struct B : A {
bool operator==(const B&) const;
};
void test() {
A a;
B b;
// 1 == a; // Doesn't work
a == 1;
// b == a; // Doesn't work; Picks `B::operator==(const B&) const;`
a == b; // Picks `A::operator==(const A&) const`, converting `b` to an `A&`.
// Equality is no longer symmetric as expected
}
In the future, with the C++20 operator<=>, you will most likely always implement this as a member function (namely as auto operator<=>(const T&) const = default;), so we know that this guideline may change.
The arguments for using non-member operator overload for symmetric operations are based on style and consistency. They are not very strong arguments 1. Non-member overloads are typically preferred because a weak argument is still a little bit better than no argument at all.
Your arguments for member operator overload don't seem to be any stronger. Consider following:
No need to befriend the function
On the other hand, if you use a non-member overload, then you don't have need to declare a member function. Is befriending the non-member somehow worse?
or to provide getters for members
There is no need for that if you befriend the overload.
It is always available to class users (although this might be the downside also)
It is unclear how this differs from the non-member overloads. Are they also not always available to the class users?
No problems with lookup (which seems to be common in our GoogleTests for some reason)
Are there lookup problems with non-member overloads? Could you demonstrate the problem with an example, and show how the problem is solved by using a member overload instead?
If it does solve the problem, then you can of course use that. Just because some guidelines recommend that you prefer one alternative as a rule of thumb, doesn't mean that is the only alternative to be used in all use cases.
1 Although, see answer https://stackoverflow.com/a/57927564/2079303 which is arguably stronger than just stylistic.
What's the rationale of Koenig lookup?
Cannot avoid thinking of it like something that makes your code a lot harder to read and more instable.
Couldn't they define Koenig lookup so that it only work for specific cases (ie: non-member operators) or when explicitly required?
The original motivation, IIRC, was to be able to write
std::cout << 42;
without having to qualify std::operator<<(std::ostream&, int) explicitely.
If you want to disable argument dependant lookup, you can explicitely qualify the function name, ie. use std::swap instead of swap to prevent swap to be looked up in whatever namespace its arguments would live.
ADL can also be used with SFINAE to test at compile time whether some function is defined for a particular type (I'll let you work this out as an exercise, there is at least one question about this on Stackoverflow).
The strongest use case for ADL is for cases like this.
namespace A
{
struct S {};
S operator+( const S&, const S& );
}
namespace B
{
A::S test()
{
A::S a, b;
return a + b;
}
}
It is also useful for selecting the correct swap function in generic code so it shouldn't only apply to operator functions. It is already a fairly complex part of the standard, making rules that prevented it from working in some cases would add further complexity, what would be the gain?
I can't think of any neat way of asking for it explicitly that would be significantly less verbose than calling a function in a different namespace directly and would, in any case, make expressions more complex.
We're you thinking something like: return [[ use_adl ]] (a + b); vs. return A::operator+( a, b ); ?