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

  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(), чтобы не было копирования/перемещения?

person balki    schedule 13.02.2013    source источник
comment
Ссылка есть ссылка.   -  person R. Martinho Fernandes    schedule 13.02.2013
comment
относительно auto&& var = func() , stackoverflow.com/questions/13618506/   -  person billz    schedule 13.02.2013
comment
@billz Ссылка — это другой случай, когда она возвращает локальную переменную в качестве ссылки. Ошибка находится внутри функции, а не там, где она вызывается.   -  person balki    schedule 13.02.2013
comment
@billz: Связанный вопрос не имеет отношения к этому, это все равно, что сказать, что const int & foo() { return 42; } int i = foo(); и int foo() { return 42; } const int & i = foo(); - это одни и те же ситуации (это не так).   -  person Luc Touraille    schedule 13.02.2013
comment
@ 6502 Да, действительно. Люди, которые не умеют обращаться со ссылками в C++03, не волшебным образом упростят себе жизнь с помощью C++11, но хорошо.   -  person Christian Rau    schedule 13.02.2013


Ответы (4)


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

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

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

между

auto&& var = func();

и

auto var = func();

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

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

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

person JoergB    schedule 13.02.2013
comment
Если функция func вернула lvalue, например Foo& func(), версия auto&& не будет копироваться, а auto будет копироваться. - person balki; 13.02.2013
comment
@balki: Но мы обсуждали здесь функцию, возвращающую значение. Теперь я сделал различие явным. - person JoergB; 13.02.2013
comment
Время жизни временного/prvalue, привязанного к ссылке, определяется областью действия ссылки. это новое в С++ 11? В c+03 это было верно только для константной ссылки. - person Suma; 13.02.2013
comment
@Suma В С++ 03 это верно для любой ссылки. Просто вы все равно не могли назначить временную ссылку на неконстантную. - person Christian Rau; 13.02.2013

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

// 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());.

person Luc Danton    schedule 13.02.2013
comment
+1, я помню, как наткнулся на пример T const& pass(T const& t) { return t; }, когда пытался улучшить обнаружение Clang возвращаемых ссылок на временные файлы... жаль, что для обнаружения возможных проблем требуется межпроцедурный анализ: x - person Matthieu M.; 13.02.2013

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

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

int foo () {
    return 42;
}

int bar () {
    const int& x = foo();
    return x; // Here is safe to use x
}
person 6502    schedule 13.02.2013
comment
Правило предназначено только для справки о константах. Я не думаю, что очевидно, что auto && является константной ссылкой, не так ли? - person Suma; 13.02.2013
comment
@Suma: ссылка — это ссылка на серверную часть компилятора. Const-корректность влияет только на внешний интерфейс (что является законным, а что нет) и не играет никакой роли в том, что является неопределенным поведением или производительностью. - person 6502; 13.02.2013
comment
Кажется, я ошибаюсь. Я ничего не вижу о const в стандарте, в соответствующем предложении говорится, когда ссылка привязана к временной в 12.2, по крайней мере, в черновике С++ 11. В учебниках говорится о const &, но это, вероятно, потому, что обычное & в любом случае не может быть привязано к временному. - person Suma; 13.02.2013
comment
@Suma: привязка неконстантной ссылки к временной является ошибкой компиляции (иногда это раздражает ... например, вы не можете передать временную функцию, которая должна вызывать неконстантные методы, и вы вынуждены использовать вместо именованной переменной, даже если вам не нужно ничего делать с ней после вызова; причина в том, что это помогает найти множество ошибок, вызванных неявными преобразованиями). - person 6502; 13.02.2013

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

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

Чтобы уточнить, единственный способ рассматривать mytup как значение r — использовать std::forward<decltype(mytup)>(mytup), как в What сообщает ли нам auto&&?; однако, если вы знаете тип mytup (в данном случае tuple<...>), вы также можете использовать std::move.

person ecatmur    schedule 13.02.2013