Martin Ayvazyan
4 min readMar 27, 2019

Since C++11, the standard provides amazing futures to implement relatively simple and high optimal solutions for memory management. However, it does not mean that there was no way to write code via old versions of the standard(C++ 1998/2003) to provide such functionality. To be able to understand the whole concepts of provided futures and use these only where they are really needed, at first there is a need to know how to implement the same behavior via the futures available only for old versions of the standard. By talking about the new futures of memory management, let's describe move semantics and forwarding.

Each C++ expression is described by two independent properties: a type and a value category. That is, every expression is exactly one of an lvalue, an xvalue, or a prvalue. Those three are, in turn, subsets of two broader value categories: glvalues (the union of lvalues and xvalues) and rvalues (the union of xvalues and prvalues). It sounds good, but to understand why such value-categorization, we need to understand what are the main usage-differences of those.

At first, should be noted, that since C++11 in the standard has been added ‘&&’ type of references, which unlike ‘&’ can bind to an rvalue like a temporary without having to be const. This simple future provides two powerful functionalities at once:

1) Move semantics — It is provided via move constructor and move assignment operator. Also, in the standard has been added std::move function, which indicates that an object may be “moved from”, i.e. allowing the efficient transfer of resources to another object. It’s implementation is very easy and can be done by hand(only for understanding the functionality):

template<typename My_T>

constexpr typename std::remove_reference<My_T>::type&& move(My_T&& obj) noexcept

{ return static_cast<typename std::remove_reference<My_T>::type&&>(obj); }

2) Forwarding — Forwarding — It is done via templates, allowing to provide ‘smart’ memory management.To provide such a functionality, in the standard has been added std::forward function — when passed forwarding reference, it overloads forward the argument to another function with the value category it had when passed to the calling function. In this case, the implementation could be provided via template’s partial specialization:

template<typename My_T>

constexpr My_T&& forward(typename std::remove_reference<My_T>::type& obj) noexcept

{ return static_cast<My_T&&>(obj); }

template<typename _Tp>

constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type&& obj) noexcept

{ static_assert(!std::is_lvalue_reference<_Tp>::value); return static_cast<_Tp&&>(obj); }

So, if pass an rvalue in the function, it’d return rvalue, if pass lvalue it’d return lvalue. The reason to use such a function is that without it, the object passed to another function would be evaluated as lvalue, even tough it was an rvalue, for example:

template<typename … Args>

void f1(Args&& … args) {f(args…);}

In case of such an implementation, in f function as argument would be passed lvalues, even though f1 has been called on rvalues. To fix the issue, there is a need to use forward

template<typename … Args>

void f1(Args&& … args) {f(std::forward<Args>(args)…);}

As you can see, both functions(move and forward) are done in compile time and don’t impact to the performance.

The next important additions in the standard are & and && specifiers for member functions. So, since C++11, it’s legal to write

1) ‘ void m_f() &&’ — the function would be called only for rvalue objects

2) ‘void m_f()&’ — the function would be called for non-rvalue objects

However, these specifiers are valid for non-static member functions only(by the same reason as const specifier for functions). It’s really very useful for controlling the function calls for objects of different value categories.

As I’ve mentioned above, forwarding works for template-specializations only:

void f(My_T&& val) { val.m_f();}

My_T obj; f(My_T()); f(obj);

As can be guessed, the first call would be passed successfully, as we have passed an rvalue, but the second one would lead to a compilation error, as has been passed an lvalue, but expected rvalue. Also, even though the first call seems works, behavior can be unexpected, as it would lead to m_f’s lvalue-version call. To fix the issue there is a need to use forwarding, i.e.:

std::forward<My_T>(val).m_f(); // in this case would be called rvalue-version of m_f

The next dark corner for perfect forwarding is templates.

template <class T>

class My_T { My_T(T&&) {/*smth.*/} };

T1 obj; My_T<T1> obj2(std::move(obj)), obj3(obj);

The constructor’s call for obj2 is legal and would be passed successfully, but in case of obj3 it would lead to a compilation error, as the constructor of My_T accepts rvalue arguments only and we’ve passed lvalue. The reason for such behavior is the constructor’s non-template specialization and usage of the class’s template parameter, which is instantiated before its call.

The solution of the ‘issue’ is provide the following implementation:

template <class T>

class My_T {

template<class T2=T>

My_T(T2&&) {/*smth.*/}

};

T1 obj; My_T<T1> obj2(std::move(obj)), obj3(obj);

In this case both constructor calls are legal, as the parameter of of My_T’s constructor is universal reference. The same behavior take in place for function implementations as well.

Conclusion

So, move semantics, forwarding and templates provide a great interface to write generic, high optimal, flexible structures. C++ is really a great choice to write high optimal, generic solutions for different kind of problems.