5
\$\begingroup\$

this is my implementation of std::forward

template<typename T, typename U>
constexpr decltype(auto) forward(U && u) noexcept
{
 return static_cast<T &&>(u);
}

compared to the standards

template<typename T>
constexpr T && forward(typename std::remove_reference<T>::type & __t) noexcept
{
 return static_cast<T &&>(__t);
}
template<typename T>
constexpr T && forward(typename std::remove_reference<T>::type && __t) noexcept
{
 return static_cast<T &&>(__t);
}

the standard uses the remove_reference to ensure usage of:

forward<T>(value)

which then requires them to overload each rvalue / lvalue instance of forward. Whereas I am using a second template parameter to ensure the correct usage, which allows me not need an overload.

can anyone point out any downfalls in my implementation?

Note: decltype(auto) can be replaced with T && for the same results.

asked Jul 7, 2017 at 2:14
\$\endgroup\$
2
  • 5
    \$\begingroup\$ You should read n2951 by Howard Hinnant. \$\endgroup\$ Commented Jul 7, 2017 at 4:34
  • 1
    \$\begingroup\$ You should ask somebody on the standard's committee. Also I bet they already have a million unit tests around the standard; why don't you find those and see if your version works for all those tests? \$\endgroup\$ Commented Jul 7, 2017 at 16:26

1 Answer 1

1
\$\begingroup\$

Your version is less type-safe than the normal implementation.

std::forward() constrains the argument's value (after dereferencing) to be of the same type as the return type.

Your implementation can perform any type conversion supported by static_cast. That allows this erroneous code to compile:

struct Base {};
struct Derived : Base {};
int main()
{
 forward<Derived>(Base{});
}

If std::forward() is used instead, we get an error:

error: no matching function for call to ‘forward<Derived>(Base)’
 14 | std::forward<Derived>(Base{});
 | ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~

We can fix the function to correctly reject the problem code. With C++20, we'd do that by adding a requires clause to constrain U:

#include <type_traits>
template<typename T, typename U>
requires (std::is_lvalue_reference_v<U> || !std::is_lvalue_reference_v<T>)
 && std::is_convertible_v<std::remove_reference_t<U>*,
 std::remove_reference_t<T>*>
constexpr decltype(auto) forward(U && u) noexcept
{
 return static_cast<T &&>(u);
}

I don't think the decltype(auto) adds clarity, so I would actually write

constexpr T&& forward(U&& u) noexcept
{
 return static_cast<T&&>(u);
}

Since the question tags say we're constrained to C++14, we'd need to reformulate the constraint as a std::enable_if:

template<typename T, typename U>
constexpr
std::enable_if_t<(std::is_lvalue_reference<U>::value
 || !std::is_lvalue_reference<T>::value)
 && std::is_convertible<std::remove_reference_t<U>*,
 std::remove_reference_t<T>*>::value,
 T&&>
forward(U&& u) noexcept
{
 return static_cast<T&&>(u);
}
answered Jan 15, 2022 at 12:24
\$\endgroup\$
1
  • \$\begingroup\$ Well, the std::forward-replacement with two template-arguments you tried to fix up is now overly restrictive. struct { operator int&(){ return data; } int data; } x; (void)std::forward<int&>(x); would no longer work. Yes, the example is contrived, but that's not the point. \$\endgroup\$ Commented Jan 22, 2022 at 22:24

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.