Правильный способ использования Decktype в качестве типа возврата следа
-
22-10-2019 - |
Вопрос
Я очень часто вижу пример этой формы:
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 Identicall, и Third Decktype (U + T) более прямой и точно выражает то, что намерения программиста не выявляют «кишки» реализации.
Что вы думаете по этому вопросу?
Решение
В общем, я не могу придумать о разумном случае использования, когда будет разница, но я думаю, что вы можете найти какой-то случай, когда операция имеет разные реализации для RVALUE-ссылок и RVALUE, и языковые правила не определяют, что возврат Тип различных перегрузков должен быть одинаковым (даже если здравый смысл будет диктовать это).
Таким образом, в общем случае не будет никакой разницы, и в случае, когда существуют различия, ну, эти случаи нуждаются в дополнительной заботе и внимании к гораздо худшим проблемам, чем сам шаблон ...
// sick corner case:
struct type {};
int operator+( type&& lhs, type&& rhs );
double operator+( type const & lhs, type const & rhs );
Я могу подумать в ситуациях, когда вы хотели бы предложить различные перегрузки для запросов RVALUE (рассмотрите некоторую реализацию списка, который предлагает operator+
В качестве объединения, тогда перегрузка с ссылками на RVALUE может избежать стоимости копирования, просто искажая указатели и оставляя аргумент, перечисляет пустые), но это было бы совершенно запутанно, если тип результата будет зависеть от L/RValue-tessess аргументов.
Другие советы
Первая версия более правильная, потому что она точно соответствует тому, что будет возвращать корпус функции. Как уже отмечалось, просто нет никакой гарантии, что
decltype(std::forward<T>(t) + std::forward<U>(u))
будет тот же тип, что и
decltype(t + u)
Возможно, это довольно угловой корпус, но «правильный» способ использовать Std :: Forward.
В пределах decltype(t + u)
, переменные t
а также u
Уже больше нет ссылок на RVALUE, они будут рассматриваться как простые ссылки на LVALU std::forward
Анкет (По крайней мере, вот как я это понимаю. Хотя может быть не так.)