...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
Like any emulation effort, the library has some limitations users should take in care to achieve portable and efficient code when using the library with C++03 conformant compilers:
When initializing base classes in move constructors, users must cast the reference to a base class reference before moving it. Example:
//Portable and efficient Derived(BOOST_RV_REF(Derived) x) // Move ctor : Base(boost::move(static_cast<Base&>(x))), mem_(boost::move(x.mem_)) { }
If casting is not performed the emulation will not move construct the base
class, because no conversion is available from BOOST_RV_REF(Derived)
to BOOST_RV_REF(Base)
.
Without the cast we might obtain a compilation error (for non-copyable types)
or a less-efficient move constructor (for copyable types):
//If Derived is copyable, then Base is copy-constructed. //If not, a compilation error is issued Derived(BOOST_RV_REF(Derived) x) // Move ctor : Base(boost::move(x)), mem_(boost::move(x.mem_)) { }
The emulation can't deal with C++0x reference collapsing rules that allow perfect forwarding:
//C++0x template<class T> void forward_function(T &&t) { inner_function(std::forward<T>(t); } //Wrong C++03 emulation template<class T> void forward_function(BOOST_RV_REF<T> t) { inner_function(boost::forward<T>(t); }
In C++03 emulation BOOST_RV_REF doesn't catch any const rlvalues. For more details on forwarding see Constructor Forwarding chapter.
The first rvalue reference proposal allowed the binding of rvalue references to lvalues:
func(Type &&t); //.... Type t; //Allowed func(t)
Later, as explained in Fixing a Safety Problem with Rvalue References this behaviour was considered dangerous and eliminated this binding so that rvalue references adhere to the principle of type-safe overloading: Every function must be type-safe in isolation, without regard to how it has been overloaded
Boost.Move can't emulate this type-safe overloading principle for C++03 compilers:
//Allowed by move emulation movable m; BOOST_RV_REF(movable) r = m;
The macro BOOST_COPYABLE_AND_MOVABLE
needs to define a copy constructor for copyable_and_movable
taking a non-const parameter in C++03 compilers:
//Generated by BOOST_COPYABLE_AND_MOVABLE copyable_and_movable &operator=(copyable_and_movable&){/**/}
Since the non-const overload of the copy constructor is generated, compiler-generated
assignment operators for classes containing copyable_and_movable
will get the non-const copy constructor overload, which will surely surprise
users:
class holder { copyable_and_movable c; }; void func(const holder& h) { holder copy_h(h); //<--- ERROR: can't convert 'const holder&' to 'holder&' //Compiler-generated copy constructor is non-const: // holder& operator(holder &) //!!! }
This limitation forces the user to define a const version of the copy assignment, in all classes holding copyable and movable classes which might annoying in some cases.
An alternative is to implement a single operator
=()
for copyable and movable classes
using
"pass by value" semantics:
T& operator=(T x) // x is a copy of the source; hard work already done { swap(*this, x); // trade our resources for x's return *this; // our (old) resources get destroyed with x }
However, "pass by value" is not optimal for classes (like containers, strings, etc.) that reuse resources (like previously allocated memory) when x is assigned from a lvalue.