执行std :: forward

son*_*yao 30 c++ c++11

我正在阅读新C++概述(C++ 11/14)(仅限PDF),在幻灯片288中它给出了一个实现std::forward:

template<typename T>                // For lvalues (T is T&),
T&& std::forward(T&& param)         // take/return lvalue refs.
{                                   // For rvalues (T is T),
    return static_cast<T&&>(param); // take/return rvalue refs.
}
Run Code Online (Sandbox Code Playgroud)

然后在文本中给出另一个实现:

通常的std :: forward实现是:

template<typename T>
struct identity {
    typedef T type;
};
template<typename T>
T&& forward(typename identity<T>::type&& param)
{ return static_cast<identity<T>::type&&>(param); }
Run Code Online (Sandbox Code Playgroud)

有什么不同?为什么后者通常是实施?

Seb*_*edl 23

第一个问题是你可以编写std::forward(x),它不能做你想要的,因为它总是产生左值引用.

第二种情况中的参数是非推导的上下文,阻止自动推导模板参数.这迫使你写std::forward<T>(x),这是正确的事情.

此外,第二个重载的参数类型应该是typename identity<T>::type&因为习惯用法的输入std::forward始终是左值.

编辑:标准实际上要求签名等同于这一个(顺便提一下,这正是libc ++所具有的):

template <class T> T&& forward(typename remove_reference<T>::type& t) noexcept;
template <class T> T&& forward(typename remove_reference<T>::type&& t) noexcept;
Run Code Online (Sandbox Code Playgroud)


Wal*_*ter 11

libc ++中的实现使用了std::remove_reference两个重载.这是源(删除一些宏后):

template <class T>
inline T&& forward(typename std::remove_reference<T>::type& t) noexcept
{
    return static_cast<T&&>(t);
}

template <class T>
inline T&& forward(typename std::remove_reference<T>::type&& t) noexcept
{
    static_assert(!std::is_lvalue_reference<T>::value,
                  "Can not forward an rvalue as an lvalue.");
    return static_cast<T&&>(t);
}
Run Code Online (Sandbox Code Playgroud)

但请注意,在C++ 14中,std::forwardconstexpr.

  • @xinwang `forward&lt;int&amp;&gt;(5)` 或 `forward&lt;std::string&amp;&gt;(std::string{})` (5认同)
  • 有没有真正的场景可以使static_assert失败? (4认同)

neu*_*ont 11

Sebastian Redl 所说的第一种情况总是会给你一个左值引用。原因是参数中的右值引用会作为左值引用传递,并且参数T&&类型是通用引用而不是右值引用。

实际上如果第一种情况是正确的,我们甚至不需要forward更多。下面是一个实验来演示通用引用参数是如何传递的

template <typename T, typename U>
void g(T&& t, U&& u)
{
    std::cout << "t is lvalue ref: "
              << std::is_lvalue_reference<decltype(t)>::value << std::endl; // 1
    std::cout << "t is rvalue ref: "
              << std::is_rvalue_reference<decltype(t)>::value << std::endl; // 0
    std::cout << "u is lvalue ref: "
              << std::is_lvalue_reference<decltype(u)>::value << std::endl; // 1
    std::cout << "u is rvalue ref: "
              << std::is_rvalue_reference<decltype(u)>::value << std::endl; // 0
}

template <typename T, typename U>
void f(T&& t, U&& u)
{
    std::cout << "t is lvalue ref: "
              << std::is_lvalue_reference<decltype(t)>::value << std::endl; // 1
    std::cout << "t is rvalue ref: "
              << std::is_rvalue_reference<decltype(t)>::value << std::endl; // 0
    std::cout << "u is lvalue ref: "
              << std::is_lvalue_reference<decltype(u)>::value << std::endl; // 0
    std::cout << "u is rvalue ref: "
              << std::is_rvalue_reference<decltype(u)>::value << std::endl; // 1

    g(t, u);
}

int main()
{
    std::unique_ptr<int> t;
    f(t, std::unique_ptr<int>());
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

该计划原来,这两个tu传递从fg是左值的引用,尽管u是在一个右值引用f。所以在第一种情况下,forwardjust的参数没有机会成为右值引用。

所述identity用于从通用参考参数类型更改为一个右值参考(如通过雷德尔提到的,它更精确至使用std::remove_reference)。然而,这种变化使得模板类型推导不再可能,因此类型参数 forforward是强制性的,因此我们将编写forward<T>(t).

但是您问题中的第二种情况也不正确,正如 Redl 也提到的,正确的方法是重载,其参数是左值引用。

我能找到的最直接的实现是这个

template <typename T>
T&& forward(typename identity<T>::type& param)
{
    return static_cast<T&&>(param);
}
Run Code Online (Sandbox Code Playgroud)

它适用于通用引用,例如

template <typename T, typename U>
void f(T&& t, U&& u)
{
    ::forward<T>(t);
    ::forward<U>(u);
}

std::unique_ptr<int> t;
f(t, std::unique_ptr<int>());
// deduction in f:
//   T = unique_ptr&, decltype(t) = unique_ptr&
//   U = unique_ptr, decltype(u) = unique_ptr&& (but treated as an lvalue reference)
// specialization of forward:
//   forward<T> = forward<unique_ptr&>, param type = unique_ptr&
//                                      return type = unique_ptr&
//   forward<U> = forward<unique_ptr>,  param type = unique_ptr&
//                                      return type = unique_ptr&&
Run Code Online (Sandbox Code Playgroud)