In C++23, it became easier to perfectly forward prvalues thanks to auto()
or auto{}
. With this new tool, is it now possible to form a FORWARD(e)
expression for an expression e
with the following requirements?
FORWARD(e)
has the same type as e
, disregarding reference qualifiersdecltype(e)
is an lvalue/rvalue reference, then FORWARD(e)
is an lvalue/xvalue respectivelyFORWARD(e)
has the same value category as e
We can do imperfect forwarding with std::forward
already:
#define FORWARD(...) ::std::forward<decltype(__VA_ARGS__)>(__VA_ARGS__)
This preserves the type and converts references appropriately.
However, a prvalue will be turned into an xvalue, because std::forward
returns an rvalue reference (i.e. FORWARD(e)
is an xvalue after conversions).
As a consequence:
T x = T(); // copy elision because x is initialized to prvalue
T x = FORWARD(T()); // calls move constructor
Is it possible to do true perfect forwarding in C++, including preservation of prvalues?
You basically want FORWARD(e)
to be e
, except if e
happens to name an rvalue reference variable, in which case you want move(e)
.
You can simply cast to the type of decltype((e))
(which if e
is a prvalue, will be elided), except when e
is an rvalue reference variable, since decltype((e))
will be an lvalue reference.
#include <type_traits>
template<typename IdType, typename ExprType>
using perfect_forward_cast = std::conditional_t<std::is_rvalue_reference_v<IdType>,
IdType,
ExprType
>;
#define FORWARD(...) ::perfect_forward_cast<decltype( __VA_ARGS__ ), decltype(( __VA_ARGS__ ))>( __VA_ARGS__ )