Now, imagine the following situation: we have a resource that cannot be copied, which should be correctly freed in a destructor, and we want to return it from a function:
descriptor_owner construct_descriptor() { return descriptor_owner("Construct using this string"); }
Actually, you can work around such situations using the swap
method:
void construct_descriptor1(descriptor_owner& ret) { descriptor_owner("Construct using this string").swap(ret); }
However, such a workaround does not allow us to use descriptor_owner
in containers. By the way, it looks awful!
It is highly recommended that you are at least familiar with the basics of C++11 rvalue references. Reading the Using C++11 move emulation recipe is also recommended.
Those readers who use C++11, already know about the move-only classes (like std::unique_ptr
or std::thread
). Using such an approach, we can make a move-only descriptor_owner
class:
class descriptor_owner1 { void* descriptor_; public: descriptor_owner1() : descriptor_(nullptr) {} explicit descriptor_owner1(const char* param); descriptor_owner1(descriptor_owner1&& param) : descriptor_(param.descriptor_) { param.descriptor_ = nullptr; } descriptor_owner1& operator=(descriptor_owner1&& param) { descriptor_owner1 tmp(std::move(param)); std::swap(descriptor_, tmp.descriptor_); return *this; } void clear() { free(descriptor_); descriptor_ = nullptr; } bool empty() const { return !descriptor_; } ~descriptor_owner1() { clear(); } }; // GCC compiles the following in C++11 and later modes. descriptor_owner1 construct_descriptor2() { return descriptor_owner1("Construct using this string"); } void foo_rv() { std::cout << "C++11n"; descriptor_owner1 desc; desc = construct_descriptor2(); assert(!desc.empty()); }
This will work only on the C++11 compatible compilers. That is the right moment for Boost.Move
! Let's modify our example, so it can be used on C++03 compilers.
According to the documentation, to write a movable but noncopyable type in portable syntax, we need to follow these simple steps:
- Put the
BOOST_MOVABLE_BUT_NOT_COPYABLE(classname)
macro in theprivate
section:
#include <boost/move/move.hpp> class descriptor_owner_movable { void* descriptor_; BOOST_MOVABLE_BUT_NOT_COPYABLE(descriptor_owner_movable
- Write a move constructor and a move assignment, taking the parameter as
BOOST_RV_REF(classname)
:
public: descriptor_owner_movable() : descriptor_(NULL) {} explicit descriptor_owner_movable(const char* param) : descriptor_(strdup(param)) {} descriptor_owner_movable( BOOST_RV_REF(descriptor_owner_movable) param ) BOOST_NOEXCEPT : descriptor_(param.descriptor_) { param.descriptor_ = NULL; } descriptor_owner_movable& operator=( BOOST_RV_REF(descriptor_owner_movable) param) BOOST_NOEXCEPT { descriptor_owner_movable tmp(boost::move(param)); std::swap(descriptor_, tmp.descriptor_); return *this; } // ... }; descriptor_owner_movable construct_descriptor3() { return descriptor_owner_movable("Construct using this string"); }
Now, we have a movable, but non copyable, class that can be used even on C++03 compilers and in Boost.Containers
:
#include <boost/container/vector.hpp> #include <your_project/descriptor_owner_movable.h> int main() { // Following code will work on C++11 and C++03 compilers descriptor_owner_movable movable; movable = construct_descriptor3(); boost::container::vector<descriptor_owner_movable> vec; vec.resize(10); vec.push_back(construct_descriptor3()); vec.back() = boost::move(vec.front()); }
Unfortunately, C++03 standard library containers still won't be able to use it (that is why we used a vector from Boost.Containers
in the previous example).
If you want to use Boost.Containers
on C++03 compilers, but standard library containers on C++11 compilers, you can do the following simple trick. Add the header file to your project with the following content:
// your_project/vector.hpp // Copyright and other stuff goes here // include guards #ifndef YOUR_PROJECT_VECTOR_HPP #define YOUR_PROJECT_VECTOR_HPP // Contains BOOST_NO_CXX11_RVALUE_REFERENCES macro. #include <boost/config.hpp> #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) // We do have rvalues #include <vector> namespace your_project_namespace { using std::vector; } // your_project_namespace #else // We do NOT have rvalues #include <boost/container/vector.hpp> namespace your_project_namespace { using boost::container::vector; } // your_project_namespace #endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) #endif // YOUR_PROJECT_VECTOR_HPP
Now, you can include <your_project/vector.hpp>
and use a vector from the namespace your_project_namespace
:
int main() { your_project_namespace::vector<descriptor_owner_movable> v; v.resize(10); v.push_back(construct_descriptor3()); v.back() = boost::move(v.front()); }
However, beware of compiler and standard library implementation-specific issues! For example, this code will compile on GCC 4.7 in C++11 mode only if you mark the move constructor, destructor, and move assignment operators with noexcept
or BOOST_NOECEPT
.
- The Reducing code size and increasing performance of user-defined type in C++11 recipe in Chapter 10, Gathering Platform and Compiler Information, provides more info on
noexcept
andBOOST_NOEXCEPT
. - More information about
Boost.Move
can be found on Boost's website http://boost.org/libs/move.