使用 decltype 作为尾随返回类型的正确方法
我经常看到这种形式的例子:
template <typename T, typename U>
auto add(T&& t, U&& u) -> decltype(std::forward<T>(t) + std::forward<U>(u))
{
return std::forward<T>(t) + std::forward<U>(u);
}
但我敢说这是更好更正确的方法:
template <typename T, typename U>
auto add(T&& t, U&& u) -> decltype(t + u)//no forwarding here
{
return std::forward<T>(t) + std::forward<U>(u);
}
为什么?首先也是最重要的,此示例中的 decltype 只需要推导出返回类型,因此 (t + u) 是返回类型而不是 (std::forward(t) + std::forward(u)),由两个 ver 生成的第二个代码是相同,第三个 decltype(u + t) 更直接,准确地表达了程序员的意图,而无需带出实现的“胆量”。
您对这个问题有何看法?
I very often see example of this form:
template <typename T, typename U>
auto add(T&& t, U&& u) -> decltype(std::forward<T>(t) + std::forward<U>(u))
{
return std::forward<T>(t) + std::forward<U>(u);
}
but I dare to say that this is better more correct way:
template <typename T, typename U>
auto add(T&& t, U&& u) -> decltype(t + u)//no forwarding here
{
return std::forward<T>(t) + std::forward<U>(u);
}
Why? First and foremost, decltype in this example has only to deduce return type so (t + u) is the return type not (std::forward(t) + std::forward(u)), second code generated by two ver is identicall, and third decltype(u + t) is more direct and expresses exactly what the intentions of a programmer are without bringing out "guts" of implementation.
What is your opinion on this subject?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
第一个版本更正确,因为它与函数体将返回的内容完全匹配。正如已经指出的那样,根本无法保证
与以下类型相同
。可以说,这是一个极端的情况,但“正确”的方法是使用 std::forward。
The first version is more correct because it matches exactly what the function body is going to return. As already pointed out, there is simply no guarantee that
will be the same type as
Arguably, this is quite a corner case, but the "correct" way is to use std::forward.
一般来说,我想不出存在差异的合理用例,但我猜您可能会发现某些情况,其中操作对右值引用和右值有不同的实现,并且语言规则并不规定返回不同重载的类型必须相同(即使常识规定它)。
因此,在一般情况下,不会有任何差异,而在存在差异的情况下,那么,这些情况需要额外的小心和注意,以解决比模板本身更糟糕的问题......
我可以考虑您会遇到的情况喜欢为右值引用提供不同的重载(考虑提供
operator+
作为串联的列表的某些实现,然后使用右值引用的重载可以通过仅修改指针并保留参数列表为空),但是如果结果的类型取决于参数的左值/右值,那将是完全混乱的。In general, I cannot think of a sensible use case where there will be a difference, but I guess that you could find some case where the operation has different implementations for rvalue-references and rvalues, and the language rules do not dictate that the return type of the different overloads has to be the same (even if common sense would dictate it).
So, in the general case, there will be no difference, and in the case where there are differences, well, those cases need extra care and attention for much worse problems than the template itself...
I can think on situations where you would like to offer different overloads for rvalue-references (consider some implementation of a list that offers
operator+
as concatenation, then an overload with rvalue references could avoid the cost of copying by just mangling with the pointers and leaving the argument lists empty), but it would be utterly confusing if the type of the result would depend on the l/rvalue-ness of the arguments.在 decltype(t + u) 中,变量 t 和 u 已经不再是右值引用,它们将被视为简单的左值引用,因此您需要额外的
std::forward
。(至少,我是这么理解的。不过可能是错的。)
Within
decltype(t + u)
, the variablest
andu
are already no rvalue references anymore, they'd get treated as simple lvalue references, hence you need the aditionalstd::forward
.(Atleast, that's how I understand it. Could be wrong though.)