Когда не использовать `auto & amp;`?

  auto&& mytup = std::make_tuple(9,1,"hello");
std::get<0>(mytup) = 42;
cout << std::get<0>(mytup) << endl;
  1. Есть ли при копировании / перемещении (без RVO) при возвращении из
    make_tuple?
  2. Это вызывает неопределенное поведение?
  3. Я могу оба прочитать и написать универсальную ссылку. Можно auto&& var =
    func()
    использовать всегда вместо auto var = func() чтобы не было копирования / перемещения?

18

Решение

  1. Да. Любой возврат из функции, которая не возвращает ссылочный тип, может включать копирование / перемещение. Eliding — это то, о чем RVO. Объект, с которым связана ваша ссылка, нужно как-то инициализировать.

  2. Нет, зачем это? Время жизни временного значения / значения, привязанного к ссылке, определяется областью действия ссылки.

  3. Если func () не возвращает ссылочный тип, не должно быть никакой разницы в эффективности (и поведении)

между

auto&& var = func();

а также

auto var = func();

В обоих случаях создается объект с временем жизни до конца содержащего блока. В одном случае он имеет собственное имя, в другом — через ссылку. В обоих случаях имя можно использовать как lvalue. RVO может одинаково хорошо применяться в любом случае.

Некоторые компиляторы могут оптимизировать для локального объекта лучше, чем для ссылки, даже если в текущем случае ссылка на временный объект на самом деле ничем не отличается от локального объекта.

Если func() может вернуть ссылку, все будет по-другому — в этом случае вы должны решить, хотите ли вы скопировать / переместить или нет.

8

Другие решения

Это только когда-либо проблематично в случае, когда инициализатор является вызовом функции, которая возвращает кратковременную ссылку на значение. С меньшим количеством слов и большим количеством кода:

// Fine; lifetime extension applies!
auto&& ref = 42;

auto id = [](int&& i) -> int&& { return std::move(i); };
auto&& uhoh = id(42);
// uhoh is now a stale reference; can't touch it!

По сравнению, auto uhoh = id(42); работал бы нормально.

В вашем случае, потому что std::make_tuple возвращает значение, а не ссылку на rvalue, проблем нет.

Я придерживаюсь мнения, что реальная опасность заключается в тех функциях и шаблонах функций со ссылочными параметрами rvalue, которые возвращают ссылку rvalue к тем из некоторых подобъектов, время жизни которых зависит от них. (При этом, что-то так просто, как auto&& ref = std::move(42); выставляет проблему!)

Ситуация не совсем новая с C ++ 11, рассмотрим: T const& ref = bar(T_factory());,

14

В C ++ есть специальное правило (даже до C ++ 11), которое гласит, что если вы связываете ссылку с временным объектом, срок действия временного элемента будет продлен до времени существования ссылки.

Более простой случай:

int foo () {
return 42;
}

int bar () {
const int& x = foo();
return x; // Here is safe to use x
}
0

Результат оценки звонка make_tuple является prvalue временным tuple конкретизации. auto Тип Спецификатор будет выводиться как то же самое tuple экземпляр (7.1.6.4p6), так mytup имеет тип tuple<...> &&, Временное значение prvalue затем увеличивается с помощью ссылки mytup (12.2p5).

  1. Потому что временный является возвращаемое значение функции, при этом копирование / перемещение не выполняются (в RVO все еще может быть make_tuple).
  2. Поведение полностью определено.
  3. Почти во всех случаях mytup будет рассматриваться как lvalue, даже если его тип является ссылкой на rvalue. Однако нет смысла использовать auto && поскольку все разумные компиляторы исключат копирование / перемещение из временного.

Чтобы уточнить, о единственном способе mytup рассматривается как значение, чтобы использовать std::forward<decltype(mytup)>(mytup), как в Что делает авто&& Расскажи нам?; Однако если вы знать тип mytup (tuple<...> в этом случае) тогда вы можете также использовать std::move,

0
По вопросам рекламы [email protected]