C++ 返回转发参考参数-最佳实践
在下面的场景中C++ 返回转发参考参数-最佳实践,c++,c++11,c++14,move-semantics,rvalue-reference,C++,C++11,C++14,Move Semantics,Rvalue Reference,在下面的场景中 template <class T> ? f(T&& a, T&& b) { return a > b ? a : b; } 模板 ? f(T&a、T&b) { 返回a>b?a:b; } 最佳回报类型是什么?到目前为止,我的想法是: 通过r值引用返回,完美地转发函数参数: template <class T> decltype(auto) f(T&& a, T&& b) {
template <class T>
? f(T&& a, T&& b)
{
return a > b ? a : b;
}
模板
? f(T&a、T&b)
{
返回a>b?a:b;
}
最佳回报类型是什么?到目前为止,我的想法是:
template <class T>
decltype(auto) f(T&& a, T&& b)
{
return a > b ? forward<T>(a) : forward<T>(b);
}
模板
decltype(自动)f(T&a、T&b)
{
返回a>b?向前(a):向前(b);
}
template <class T>
auto f(T&& a, T&& b)
{
return a > b ? forward<T>(a) : forward<T>(b);
}
模板
自动f(T&a、T&b)
{
返回a>b?向前(a):向前(b);
}
这些解决方案有问题吗?有更好的吗?最佳做法是什么 这取决于你的意图和你的
T
的移动意识。每种情况都是有效的,但行为上存在差异;这:
template <class T>
decltype(auto) f(T&& a, T&& b)
{
return a > b ? forward<T>(a) : forward<T>(b);
}
将移动构造一个值(auto
将永远不会导出&
或&
),如果T
不可移动构造,则将调用复制构造函数(如果T
是左值引用,则将始终进行复制)。如果在表达式中使用f
,那么最终会生成一个xvalue(这让我想知道编译器是否可以使用初始右值,但我不会打赌)
长话短说,如果使用
f
的代码是以同时处理右值和左值引用的方式构建的,那么我会选择第一种方法,毕竟这是逻辑std::forward
构建的,而且当您有左值引用时,您不会生成任何副本。对于选项1,当两个参数具有不同的值类别时,需要使用两个模板参数来启用转发。应该是
template <typename T, typename U>
decltype(auto) f(T&& a, U&& b)
{
return a > b ? std::forward<T>(a) : std::forward<U>(b);
}
模板
数据类型(自动)f(T&a、U&b)
{
返回a>b?标准::转发(a):标准::转发(b);
}
这里实际返回的内容很难计算。首先,您需要知道a
和b
的值类别,以及T
和U
的推导结果。然后,无论你选择什么配对,都要经过三元运算符的非常复杂的规则。最后,它的输出经过decltype
规则,为您提供函数的实际返回类型
我确实曾经坐下来把这一切都解决过。据我记忆所及,它是这样的:当且仅当a
和b
是兼容类型的左值引用时,结果将是可变的左值引用;如果a
和b
中的一个是常量左值引用,而另一个是任何类型的引用,则结果将是常量左值引用;否则,f
将返回一个新值
换句话说,它对任何给定的两个参数都是正确的——可能是因为有人坐下来写规则,这样它在所有情况下都是正确的
选项2的操作与选项1完全相同,只是decltype
规则不起作用,函数将始终返回一个新值--plainauto
从不推断为引用
我想不出一个选项3能在RVO方面起作用,因为正如你说的,你正在使用参数
总而言之,我认为(1)是您正在寻找的答案。您如何期望有一个可以返回两种不同类型的返回类型?T还是U?@xaxxon说T是
int
,U
是double
。三元运算符的类型是什么?我会让你从那里捡起来,你拿到gist@xaxxon只要T
可以转换为U
(或相反,或根据标准定义的更复杂的内容),这是完全有效的。@Ajay因为字符a、b;decltype(a+b)c;std::cout模板decltype(自动)f(T&&a,U&&b)
可以;或者,您可以将其设置为不可用,template auto f(T&&a,U&&b)->decltype(a>b?forward(a):forward(b))
。在这两种情况下,编译器(遵循确定条件运算符的两个操作数的公共类型的规则)将找出适当的返回类型(如果有)。据我所知,最初的问题被编辑为删除U
,以避免混淆,远离SFINAE和类型推广事宜,专注于正确使用完美转发的实际主题,移动语义和右值引用。这回答了你的问题吗?(另一个答案引用了问题原始版本中的代码)不,我的意思是,在任何情况下,当值类别(和类型)不同于参数时,两次使用T
都可能导致扣减失败expressions@PiotrSkotnicki尼科斯的假设是正确的。我不在乎有2、3或4种类型,我只是在问如何从函数中获取值,我应该前进还是移动来构造返回值?也许我应该编辑为只有一个参数?(除此之外,编辑后,f
的签名是给定的,我不需要答案来改变这一点,我假设这个答案是“如果你有签名,那么……”@LorahAttkins这不仅仅是一个等价类型的问题,而且也是一个等价类别的问题。如果你想避免混淆,那么使用单参数函数将是一个更好的例子。我同意@PiotrSkotnicki的观点。我认为这个答案不是很有用,因为像这样的简单代码无法编译。
template <typename T, typename U>
decltype(auto) f(T&& a, U&& b)
{
return a > b ? std::forward<T>(a) : std::forward<U>(b);
}