从 C++11 开始理解 std::forward 的实现

Understanding of the implementation of std::forward since C++11

constexprnoexcept 被排除在外,因为它们似乎与理解 std::forward 的行为无关。)

根据我对 Scott Meyers 的理解 "Effective Modern C++", std::move 在 C++14 中的示例实现如下

template<typename T>
decltype(auto) move(T&& param) {
  return static_cast<remove_reference_t<T>&&>(param);
}

考虑到什么是转发(或 "universal")引用,我认为这个实现对我来说非常清楚:

简而言之,我对move实现中使用forwarding/universal引用的理解如下:

很高兴知道到目前为止我的理解是否正确。

另一方面,std::forward 在 C++14 中的示例实现如下

template<typename T>
T&& forward(remove_reference_t<T>& param) {
  return static_cast<T&&>(param);
}

我的理解是这样的:

我的疑惑如下

作为附带问题:

forward本质上是一种在完美转发中保存价值类别的机制。

考虑一个简单的函数,它试图透明地调用 f 函数,尊重值类别。

template <class T>
decltype(auto) g(T&& arg)
{
    return f(arg);
}

这里的问题是,无论arg是否是右值引用类型,表达式arg总是一个左值。这是 forward 派上用场的地方:

template <class T>
decltype(auto) g(T&& arg)
{
    return f(forward<T>(arg));
}

考虑 std::forward 的参考实现:

template <class T>
constexpr T&& forward(remove_reference_t<T>& t) noexcept
{
    return static_cast<T&&>(t);
}

template <class T>
constexpr T&& forward(remove_reference_t<T>&& t) noexcept
{
    static_assert(!std::is_lvalue_reference_v<T>);
    return static_cast<T&&>(t);
}

(你可以在这里使用decltype(auto),因为推导的类型总是T&&。)

在以下所有情况下,将调用第一个重载,因为表达式 arg 表示一个变量,因此是一个左值:

  • 如果使用非常量左值调用 g,则 T 被推导为非常量左值引用类型。 T&&T 相同,forward<T>(arg) 是非常量左值表达式。因此,f 是用非常量左值表达式调用的。

  • 如果使用 const 左值调用 g,则 T 被推导为 const 左值引用类型。 T&&T 相同,而 forward<T>(arg) 是一个 const 左值表达式。因此,f 是用 const 左值表达式调用的。

  • 如果使用右值调用 g,则 T 被推断为非引用类型。 T&& 是右值引用类型,forward<T>(arg) 是右值表达式。因此,f 是用右值表达式调用的。

在所有情况下,都尊重值类别。

普通完美转发没有使用二次重载。请参阅 了解其用法。