std::forward 的第二次重载(cppreference.com 上的示例)

问题描述 投票:0回答:1

我知道

std::forward
的第二次超载:

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

用于右值(如 Howard Hinnant 在他的回答中所述:std::forward 如何接收正确的参数?

cppreference.com 有一个何时使用此重载的示例(Praetorian 的std::forward 如何接收正确的参数? 中也提到过):

  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<decltype(forward<T>(arg).get())>

Cpp参考状态:

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

举个例子:

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()));

尽管如此,我还是无法理解为什么有人需要添加外部转发。

编辑:编辑移至后续问题:std::forward 的 RValue 引用重载可能导致悬空引用?

c++ c++11 perfect-forwarding
1个回答
2
投票

为什么还需要外前锋

forward<decltype(forward<T>(arg).get())>

不是。该表达式已经属于其自己的正确值类别。在 C++17 中(当按值返回更大的类型时),这甚至是一种悲观。它所做的就是将潜在的纯右值转换为 x 值,并抑制复制省略。我很想说这是货物崇拜编程。

© www.soinside.com 2019 - 2024. All rights reserved.