Правильный способ использовать decltype в качестве конечного типа возврата - PullRequest
4 голосов
/ 27 апреля 2011

Я очень часто вижу пример этой формы:

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)), второй код, сгенерированный двумя вершинами, является идентичные и третий decltype (u + t) являются более прямыми и выражают именно то, что намерения программиста, не раскрывая «кишки» реализации.

Каково ваше мнение по этому вопросу?

Ответы [ 3 ]

3 голосов
/ 27 апреля 2011

Первая версия более правильная, потому что она точно соответствует тому, что собирается вернуть тело функции. Как уже указывалось, просто нет гарантии, что

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

будет того же типа, что и

decltype(t + u)

Возможно, это довольно сложный случай, но "правильный" способ - использовать std :: forward.

2 голосов
/ 27 апреля 2011

В общем, я не могу придумать разумного варианта использования, где будет разница, но я предполагаю, что вы могли бы найти случай, когда операция имеет разные реализации для rvalue-ссылок и rvalues, а языковые правила не диктуют что тип возвращаемого значения различных перегрузок должен быть одинаковым (даже если здравый смысл диктует это).

Таким образом, в общем случае различий не будет, а в случае различий, эти случаи требуют особого внимания и заботы о гораздо худших проблемах, чем сам шаблон ...

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

Я могу подумать о ситуациях, когда вы хотели бы предложить различные перегрузки для ссылок на rvalue (рассмотрите некоторую реализацию списка, который предлагает operator+ как конкатенацию, тогда перегрузка с ссылками на rvalue могла бы избежать затрат на копирование путем простого искажения с указателями и оставляя списки аргументов пустыми), но было бы крайне странно, если бы тип результата зависел от l / rvalue-ness аргументов.

2 голосов
/ 27 апреля 2011

В decltype(t + u) переменные t и u уже не являются ссылками rvalue, они будут рассматриваться как простые ссылки lvalue, поэтому вам понадобится дополнительная std::forward. (По крайней мере, я так понимаю. Может быть, это неправильно.)

...