В чём разница между decltype(auto) и auto&&?

100
27 ноября 2020, 11:50

В чём разница между decltype(auto) и auto&&?

Понятно, что типы вычисляются по разным механизмам, но есть ли разница в конечном результате?

Answer 1

Раз типы вычисляются по разным механизмам, то и разница в конечном результате присутствует, иначе зачем тогда было бы два способа (хотя в С++ куча мест где без причин есть несколько способов сделать одно и то же)?

#include <type_traits>
class foo {};
foo bar() { return foo{}; }
decltype(auto) var1{bar()};
auto && var2{bar()};
static_assert(::std::is_same_v<decltype(var1), class foo>);
static_assert(::std::is_same_v<decltype(var2), class foo &&>);
Answer 2

Безусловно разница есть, но в некоторых случаях могут быть и одинаковые результаты. Ничего удивительного в том, что разные подходы могут при некотором стечении обстоятельств давать одинаковые результаты быть не должно. Пример:

#include <type_traits>
int& f() 
{
    static int i = 42;
    return i;
}
int g() { return 42; }
int main()
{
    auto&& v1 = f();
    decltype(auto) v2 = f();
    auto&& v3 = g();
    decltype(auto) v4 = g();    
    static_assert(std::is_same<decltype(v1), int&>::value);
    static_assert(std::is_same<decltype(v2), int&>::value);
    static_assert(std::is_same<decltype(v3), int&&>::value);
    static_assert(std::is_same<decltype(v4), int>::value);
}

Для переменной, используемой в качестве результата f() и auto&& и decltype(auto) дадут одинаковые типы - int&. С функцией g() уже будут другие результаты - int&& и int соответственно.

decltype(auto) даёт тип объявления сущности, т.е. в случае функций объявленный тип результата: int& для f(), и int для g(). Подробнее можно посмотреть тут.

В свою очередь вывод для auto&& будет работать по схеме вывода типа параметра в шаблоне следующего вида:

template <class T>
void t(T&& a); // T вместо auto

Если вызвать t(f()) то тип a будет выведен как int&, а для t(g()) получится int&&.

Answer 3

Выражение вида auto&& foo = make_foo(params); будет корректно в большинстве случаев, за исключением некоторых прокси-объектов. Если make_foo возвращает тип bar, foo будет иметь тип bar&&, при этом время жизни возвращаемого значения будет расширено до времени жизни foo. Т.е. это корректно, как и в таком случае:

std::string&& str = std::string("foo");
std::string str2 = std::move(str);

Если make_foo возвращает bar&, const bar& или const bar&, foo будет иметь соответствующий тип. Это полезно, например, при работе с кортежами:

std::tuple<bar&, const bar&, bar&&> t = ...;
auto&& r1 = std::get<0>(t); // bar&
auto&& r2 = std::get<1>(t); // const bar&
auto&& r3 = std::get<2>(t); // bar&&

В случае decltype(auto) это перестаёт работать. В выражении decltype(auto) foo = make_foo(params);, если make_foo возвращает bar, foo будет иметь тип bar, т.е. значение будет сразу перемещено или будет применена оптимизация возвращаемого значения, что не всегда желательно. Пример:

auto&& foo= makeBar(); // Bar&&
// перемещение или инициализация на месте, Эквивалентно vector.emplace_back(makeBar())
vector.emplace_back(foo); 

Или

decltype(auto) baz = makeBar(); // Перемещение или инициализация на месте
vector.emplace_back(baz); // Копирование
READ ALSO
константное значение n, m

константное значение n, m

Пытаюсь получить транспонированную матрицу, но выдает ошибку Выражение должно иметь константное значение n, m; Не могу понять что не так, пример...

144
Python extension modules без линковки к pythonXX.dll

Python extension modules без линковки к pythonXX.dll

Возможно ли использовать импортируемые dll (имеющие расширениеpyd) в питоне на Windows без линковки к pythonXX

94
Задание имени библиотеки, используя generator expressions

Задание имени библиотеки, используя generator expressions

Пытаюсь задать название библиотеки, используя generator expressions, однако получаю ошибку сборки:

149
C++ перегрузка оператора = для объекта с shared_ptr

C++ перегрузка оператора = для объекта с shared_ptr

Создал класс, который реализует массив на shared_ptrХочу перегрузить операцию равно, внутри метода все работает - массив получает новый размер...

97