Post by Marcel MuellerPost by Andrey TarasevichFirstly, when implementing a custom `swap` for your custom type it is
a better idea to implement it as a friend function
Sometimes it can be useful to have a swap member function.
E.g. when implementing assignment functions/operators the pattern
Foo(whatever_arguments).swap(*this);
cannot be simply converted to a free swap function because the
constructor does does not bind the the reference argument. You always
need a named variable in this case.
This is an old and a well-known asymmetry in how overloaded operators
behave in C++. And fixing this asymmetry is actually one of the
side-applications of rvalue references in C++11.
Before C++11 the problem you described could also be observed in the
following example
#include <string>
#include <fstream>
int main()
{
std::string hello = "Hello";
std::ofstream("text.txt") << hello << 123 << std::endl; // 1
std::ofstream("text.txt") << 123 << hello << std::endl; // 2
}
Lines 1 and 2 look very similar. However, line 1 triggers an error
before C++11, while line 2 compiles fine.
`operator <<` for `std::string` is overloaded by a standalone function,
which expects `std::ostream &` (a non-const lvalue reference) as its
first parameter. This immediately means that a temporary object cannot
be used as its argument. For this reason sub-expression
`std::ofstream("text2.txt") << hello` is already invalid.
Meanwhile, line 2 is valid even in C++98. In this case a member overload
of `operator <<` for `int` argument is invoked first. There's no
restrictions on invoking non-const member functions for temporaries.
That invocation returns a `std::ostream &`, which can then be
successfully used as an argument for `operator <<` for `std::string`.
So, in this case the initial `<<` for `int` saves the day as an
accidental "rvalue-to-lvalue" converter.
In C++11 an additional template overload for `<<` has been added to the
library (see [ostream.rvalue])
template <class charT, class traits, class T>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>&& os, const T& x);
which simply does `os << x`, thus taking up the role of rvalue-to-lvalue
converter. Line 1 above becomes valid in C++11.
--
Best regards,
Andrey