返回转发参考参数 - 最佳实践

Lor*_*ins 11 c++ rvalue-reference move-semantics c++11 c++14

在以下场景中

template <class T>
? f(T&& a, T&& b)
{
    return a > b ? a : b; 
}
Run Code Online (Sandbox Code Playgroud)

最佳回报类型是什么?到目前为止我的想法是:

  1. 返回r值refs,完美转发函数参数:

    template <class T>
    decltype(auto) f(T&& a, T&& b)
    {
        return a > b ? forward<T>(a) : forward<T>(b); 
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 移动构造返回值:

    template <class T>
    auto f(T&& a, T&& b)
    {
        return a > b ? forward<T>(a) : forward<T>(b); 
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 尝试找到一种方法来启用(N)RVO(即使我认为因为我想使用不可能的函数参数)

这些解决方案有问题吗?还有更好的吗?什么是最佳做法?

Nik*_*iou 5

这取决于你的意图和你的行动意识T。每个案例都是有效的,但行为上存在差异;这 :

template <class T>
decltype(auto) f(T&& a, T&& b)
{
    return a > b ? forward<T>(a) : forward<T>(b); 
}
Run Code Online (Sandbox Code Playgroud)

将完美地转发左值或右值引用,从而根本不会产生临时值。如果您将其放在inline那里(或者编译器为您放置它)就像将参数放在适当的位置一样。与“第一眼直觉”相反,它不会由于“引用”死堆栈对象(对于右值情况)而产生任何运行时错误,因为我们转发的任何临时对象都来自我们之上(或之下,取决于您如何在函数调用期间考虑堆栈)。另一方面,(在右值情况下):

template <class T>
auto f(T&& a, T&& b)
{
    return a > b ? forward<T>(a) : forward<T>(b); 
}
Run Code Online (Sandbox Code Playgroud)

将移动构造一个值(auto永远不会推导出&or &&),如果不可移动构造,那么您将调用复制构造函数(如果是左值引用,T则始终会创建副本)。T如果你f在表达式中使用,你毕竟会产生一个xvalue(这让我想知道编译器是否可以使用初始右值,但我不会打赌)。

长话短说,如果使用的代码f是以处理右值和左值引用的方式构建的,我会选择第一个选择,毕竟逻辑是std::forward围绕这个构建的,而且当你有左值引用。


Tri*_*dle 5

对于选项1,您需要使用两个模板参数,以便在两个参数具有不同的值类别时启用转发.它应该是

template <typename T, typename U>
decltype(auto) f(T&& a, U&& b)
{
    return a > b ? std::forward<T>(a) : std::forward<U>(b);
}
Run Code Online (Sandbox Code Playgroud)

实际返回的内容非常难以解决.首先,你需要知道的价值类ab,什么TU得到推导出.然后,无论您选择哪种配对,都要经历三元运算符的非常复杂的规则.最后,它的输出通过decltype规则来为您提供函数的实际返回类型.

实际上我确实坐了下来,在SO的帮助下完成了一次.就我的记忆而言,它是这样的:结果将是一个可变的左值引用,当且仅当ab是兼容类型的左值引用时; 如果其中一个ab是一个const左值引用而另一个是任何类型的引用,则结果将是一个const左值引用; 否则f将返回一个新值.

换句话说,它对任何给定的参数做了正确的事情- 可能是因为有人坐下来准确地编写规则,以便在所有情况下做正确的事情.

选项2与选项1完全相同,只是decltype规则没有进入,并且该函数将始终返回一个新值 - 普通auto永远不会推导到引用.

我想不出一个可以在RVO方面工作的选项3,因为你说你正在使用这些参数.

总而言之,我认为(1)是您正在寻找的答案.