I want to write a function identity
which perfectly forwards its argument without any copy. I would write something like this
template< typename V >
inline V&& identity( V&& v )
{
return std::forward< V >( v );
}
But is it correct? Does it return always the correct type? Does it simply forward v
independently if it is a lvalue/lvalue reference/temporary?
You can use a parameter pack as a guard, so that nobody can actually force a type that is different from the one that would have been deduced otherwise.
As a minimal, working example:
#include <type_traits>
#include <utility>
template<typename..., typename V>
constexpr V&& identity(V&& v) {
return std::forward<V>(v);
}
int main() {
static_assert(std::is_same<decltype(identity(42)), int&&>::value, "!");
static_assert(std::is_same<decltype(identity<int>(42)), int&&>::value, "!");
static_assert(std::is_same<decltype(identity<float>(42)), int&&>::value, "!");
// here is the example mentioned by @Jarod42 in the comments to the question
static_assert(std::is_same<decltype(identity<const int &>(42)), int&&>::value, "!");
}
This way, the return type depends on the actual type of the v
parameter and you cannot force a copy in any way.
As mentioned in the comments by @bolov, the syntax could quickly become obscure.
Anyway, as suggested by @Jarod42, it's a matter of being more explicit about what we are doing.
As an example:
template <typename... EmptyPack, typename V, typename = std::enable_if_t<sizeof...(EmptyPack) == 0>>
As an alternative:
template <typename... EmptyPack, typename V, std::enable_if_t<sizeof...(EmptyPack) == 0>* = nullptr>
Or even:
template <typename... EmptyPack, typename V>
constexpr
std::enable_if_t<sizeof...(EmptyPack) == 0, V&&>
identity(V&& v) {
return std::forward<V>(v);
}
Pick your preferred one up and use it, they should be all valid in this case.