【问题标题】:Second overload of std::foward (example on cppreference.com)std::foward 的第二次重载(cppreference.com 上的示例)
【发布时间】:2021-08-04 13:07:44
【问题描述】:

我知道std::forward的第二次重载:

template< class T >
constexpr T&& forward( std::remove_reference_t<T>&& t ) noexcept;

用于右值(如 Howard Hinnant 在他的回答中所述:How does std::forward receive the correct argument?

cppreference.com 使用此重载时有一个示例(Praetorian 在How does std::forward receive the correct argument? 中也提到过):

  1. 将右值作为右值转发并禁止将右值作为左值转发 此重载可以将表达式(例如函数调用)的结果(可能是右值或左值)转发为转发引用参数的原始值类别.

例如,如果一个包装器不只是转发它的参数,而是在参数上调用一个成员函数,并转发它的结果:

// transforming wrapper 
template<class T>
void wrapper(T&& arg)
{
    foo(forward<decltype(forward<T>(arg).get())>(forward<T>(arg).get()));
}

arg 的类型可能在哪里

struct Arg
{
    int i = 1;
    int  get() && { return i; } // call to this overload is rvalue
    int& get() &  { return i; } // call to this overload is lvalue
};

我真的不明白这个例子。为什么还需要外前锋forward&lt;decltype(forward&lt;T&gt;(arg).get())&gt;

Cppreference 状态:

此重载可以将表达式(例如函数调用)的结果(可能是右值或左值)转发为转发引用参数的原始值类别。

举个例子:

void func(int& lvalue)
{
    std::cout << "I got an lvalue!" << std::endl;
}

void func(int&& rvalue)
{
    std::cout << "I got an rvalue!" << std::endl;
}

template <typename T>
T&& myForward(typename std::remove_reference_t<T>& t)
{
    return static_cast<T&&>(t);
}

struct foo
{
    int i = 42;
    int& get()& { return i; }
    int get()&& { return i; }
};

template <typename T>
void wrapper(T&& t)
{

    func(myForward<T>(t).get());
}

int main()
{
    foo f;
    wrapper(f);
    wrapper(foo());

    return 0;
}

打印出来:

I got an lvalue!
I got an rvalue!

很好,没有外部转发,同时它也转发“表达式的结果 [...] 作为转发引用参数的原始值类别”。它甚至不需要std::forward 的第二次重载。只有在像这样调用func() 时才需要这种重载:

func(myForward<decltype(myForward<T>(t).get())>(myForward<T>(t).get()));

不过,我无法理解为什么有人需要添加外前锋。

编辑:编辑移至后续问题:RValue-reference overload of std::forward potentially causing dangling reference?

【问题讨论】:

  • 您引用了一些未知来​​源。在包含“示例,如果包装器没有”的引号上方搜索该字符串的链接不显示该字符串?上面的“引用”部分应该是引用,还是只是格式错误?
  • 你在假装引用的同时进行释义吗?我很困惑吗?你放错链接了吗?
  • 这可能只是复杂模板编程场景中可能发生的一个示例。进一步简化:我们可能希望像myForward&lt;int&gt;(42); 这样的代码能够编译,如果没有std::remove_reference_t&lt;T&gt;&amp;&amp; t 重载,它将无法编译。

标签: c++ c++11 perfect-forwarding


【解决方案1】:

为什么还需要外部前锋forward&lt;decltype(forward&lt;T&gt;(arg).get())&gt;

It's not。该表达式已经是它自己的正确值类别。在 C++17 中(当按值返回更大的类型时)它甚至是一种悲观。它所做的只是将一个潜在的prvalue 变成一个xvalue,and inhibiting copy elision。我很想说这是货物崇拜编程。

【讨论】:

  • 感谢您的回答!它让我更仔细地了解了完整表达式中涉及的值类别。因此,我想知道 std::forward 的第二个重载在与 prvalues 一起使用时是否会导致悬空引用(请参阅我的编辑)。
  • 感谢您强调这一点!将编辑移至后续问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-26
  • 1970-01-01
  • 2011-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多