c++ c++11 auto universal-reference

c++ - ¿Cuándo no usar `auto &&`?



c++11 universal-reference (4)

  1. Sí. Cualquier devolución de una función que no devuelva un tipo de referencia puede implicar una copia / movimiento. Eliding es de eso se trata RVO. El objeto al que está vinculada su referencia debe inicializarse de alguna manera.

  2. No. ¿Por qué debería? El tiempo de vida de un temporal / prvalue vinculado a una referencia está determinado por el alcance de la referencia.

  3. Si func () no devuelve un tipo de referencia, no debería haber ninguna diferencia en la eficiencia (ni en el comportamiento)

Entre

auto&& var = func();

y

auto var = func();

En ambos casos, se construye un objeto con vida útil hasta el final del bloque contenedor. En un caso, tiene su propio nombre, en el otro se nombra a través de una referencia. En ambos casos, el nombre se puede utilizar como un valor l. RVO puede aplicarse igualmente bien en cualquier caso.

Algunos compiladores podrían optimizar mejor para un objeto local que para una referencia, aunque en el caso actual la referencia a temporal no es realmente diferente de un objeto local.

Si func() puede devolver una referencia, las cosas son muy diferentes; en ese caso, debe decidir si desea copiar / mover o no.

auto&& mytup = std::make_tuple(9,1,"hello"); std::get<0>(mytup) = 42; cout << std::get<0>(mytup) << endl;

  1. ¿Hay una copia / movimiento involucrado (sin RVO) al regresar de make_tuple?
  2. ¿Está causando un comportamiento indefinido?
  3. Puedo leer tanto escribir la referencia universal. ¿Se puede usar auto&& var = func() siempre en lugar de auto var = func() para que no haya copia / movimiento?

El resultado de evaluar la llamada a make_tuple es un prvalue temporal de una instanciación de tuple . El especificador auto tipo se deducirá como la misma instanciación de tuple (7.1.6.4p6), por lo que mytup es de tipo tuple<...> && . El prvalue temporal se extiende luego de por vida con la referencia mytup (12.2p5).

  1. Debido a que el valor temporal es el valor de retorno de la función, no hay copia / movimiento involucrado (todavía puede haber RVO dentro de make_tuple ).
  2. El comportamiento está completamente definido.
  3. Para casi todos los casos, mytup será tratado como un lvalue aunque su tipo sea una referencia de rvalue. Sin embargo, no tiene sentido utilizar auto && ya que todos los compiladores sensibles ocultarán la copia / movimiento del temporal.

Para aclarar, la única forma de que mytup tratado como un valor es usar std::forward<decltype(mytup)>(mytup) , como en ¿Qué nos dice auto &&? ; sin embargo, si conoce el tipo de mytup ( tuple<...> en este caso), entonces también puede usar std::move .


Existe una regla específica en C ++ (incluso antes de C ++ 11) que dice que si vincula una referencia a un temporal, la duración del temporal se extenderá a la duración de la referencia.

Un caso más simple es:

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


Solo es problemático en el caso de que el inicializador sea una llamada de función que devuelva una referencia de valor de corta duración. Con menos palabras y más código:

// 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!

En contraste, auto uhoh = id(42); Habría funcionado bien.

En su caso, porque std::make_tuple devuelve un valor y no una referencia rvalue, no hay problema.

Soy de la opinión de que el peligro real proviene de esas funciones y plantillas de función con parámetros de referencia de valor y que devuelven una referencia de valor a cualquiera de los subobjetos de los cuales la vida útil depende de ellos. (Dicho esto, algo tan simple como auto&& ref = std::move(42); muestra el problema!)

La situación no es completamente nueva en C ++ 11, considere: T const& ref = bar(T_factory()); .