开发者

Correct way to use decltype as trailing return type

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 开发者_Go百科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?


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

decltype(std::forward<T>(t) + std::forward<U>(u))

will be the same type as

decltype(t + u)

Arguably, this is quite a corner case, but the "correct" way is to use std::forward.


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...

// sick corner case:
struct type {};
int operator+( type&& lhs, type&& rhs );
double operator+( type const & lhs, type const & rhs );

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.


Within decltype(t + u), the variables t and u are already no rvalue references anymore, they'd get treated as simple lvalue references, hence you need the aditional std::forward. (Atleast, that's how I understand it. Could be wrong though.)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜