C ++ auto y vs auto

88

Al crear variables locales, ¿es correcto usar (const) auto&o auto?

p.ej:

SomeClass object;
const auto result = object.SomeMethod();

o const auto& result = object.SomeMethod();

Donde SomeMethod () devuelve un valor no primitivo, tal vez otro tipo definido por el usuario. Tengo entendido que const auto& resultes correcto ya que el resultado devuelto por SomeMethod () llamaría al constructor de copia para el tipo devuelto. Por favor, corríjame si estoy equivocado.

¿Qué pasa con los tipos primitivos? Supongo que const auto sum = 1 + 2;es correcto.

¿Esto también se aplica al rango basado en bucles?

for(const auto& object : objects)
rohunb
fuente
1
Le recomiendo encarecidamente que lea esto: safaribooksonline.com/library/view/effective-modern-c/… Los primeros dos capítulos son gratuitos y describen la deducción del tipo de plantilla, que es esencialmente cómo autofunciona (excepto en el caso peculiar de initializer_lists, que son no deducido en un contexto de plantilla), luego autoescriba deducción.
vsoftco

Respuestas:

102

autoy auto &&cubren la mayoría de los casos:

  • Úselo autocuando necesite una copia local. Esto nunca producirá una referencia. El constructor de copia (o movimiento) debe existir, pero es posible que no se llame debido a la optimización de elisión de copia .

  • Úselo auto &&cuando no le importe si el objeto es local o no. Técnicamente, esto siempre producirá una referencia, pero si el inicializador es temporal (por ejemplo, la función regresa por valor), se comportará esencialmente como su propio objeto local.

    Además, auto &&tampoco garantiza que el objeto sea modificable. Dado un constobjeto o referencia, lo deducirá const. Sin embargo, a menudo se asume la modificabilidad, dado el contexto específico.

auto &y auto const &son un poco más específicos:

  • auto &garantiza que está compartiendo la variable con otra cosa. Siempre es una referencia y nunca temporal.

  • auto const &es similar auto &&, pero proporciona acceso de solo lectura.

¿Qué pasa con los tipos primitivos / no primitivos?

No hay diferencia.

¿Esto también se aplica al rango basado en bucles?

Si. Aplicando los principios anteriores,

  • Úselo auto &&para la capacidad de modificar y descartar valores de la secuencia dentro del bucle. (Es decir, a menos que el contenedor proporcione una vista de solo lectura, por ejemplo std::initializer_list, en cuyo caso será efectivamente una auto const &.)
  • Úselo auto &para modificar los valores de la secuencia de una manera significativa.
  • Úselo auto const &para acceso de solo lectura.
  • Úselo autopara trabajar con copias (modificables).

También lo mencionas auto constsin referencia. Esto funciona, pero no se usa con mucha frecuencia porque rara vez hay una ventaja en el acceso de solo lectura a algo que ya posee.

Potatoswatter
fuente
Sutter dice que auto y pistas constness
Jesse Pepper
1
@JessePepper Sí. (Sin embargo, es un poco extraño apelar a la autoridad). ¿Se refiere a una parte específica de esto?
Potatoswatter
En esencia auto&parece ser una buena elección. Pero const auto&utilícelo cuando desee agregar constness que aún no está allí. auto&&es para reenvío, que creo que ocurre con más frecuencia de lo que piensa Sutter. Por ejemplo, es posible que desee almacenar un valor de retorno auto&&y luego "reenviarlo" a dos cosas, como std :: cout para ver el valor de depuración y también pasarlo a alguna otra función. Solía ​​usar auto && con más frecuencia, pero me ha mordido una o dos veces cuando hacía cosas inesperadas. ¡Ojalá me diera más cuenta de lo que salió mal!
Jesse Pepper
@JessePepper Sí, auto&&está relacionado con una característica de lenguaje faltante, que debería permitir que cualquier declaración se divida en declaraciones más pequeñas. La parte que falta es la extensión de la vida útil ... Me esforcé un poco en solucionar esto, pero nadie se dio cuenta. No pongas demasiado valor en los expertos :)
Potatoswatter
En auto const: puede ser más natural escribir auto const x = fn (); si sabe que la función no devuelve una referencia y desea que el objeto x no cambie, para evitar errores o documente su uso en el alcance. De la misma manera, normalmente no escribiría: const int & x = 1; Pero auto const & da resultados equivalentes debido a las reglas de extensión de por vida para las referencias declaradas de esta manera.
Spacen Jasset
47

Sí, es correcto de usar autoy auto&para variables locales. Al obtener el tipo de retorno de una función, también es correcto usarlo auto&. Esto también se aplica al rango basado en bucles for.

Las reglas generales de uso autoson:

  • Elija auto xcuándo desea trabajar con copias.
  • Elija auto &xcuándo desea trabajar con elementos originales y puede modificarlos.
  • Elija auto const &xcuándo desea trabajar con elementos originales y no los modificará.

Puede leer más sobre el especificador automático aquí .

fantasma
fuente
9

autoutiliza el mismo mecanismo de deducción de tipos que las plantillas, la única excepción que conozco es la de las listas brace-init, que se deducen autocomo std::initializer_list, pero no se deducen en un contexto de plantilla.

auto x = expression;

funciona quitando primero todos los calificadores de referencia y cv del tipo de la expresión del lado derecho, luego haciendo coincidir el tipo. Por ejemplo, si tiene const int& f(){...}, auto x = f();deduce xcomo inty no const int& .

La otra forma,

auto& x = expression

no elimina los calificadores cv, por lo que, utilizando el ejemplo anterior, auto& x = f()deduce xcomo const int&. Las otras combinaciones solo agregan calificadores de cv.

Si desea que su tipo siempre se deduzca con calificadores cv-ref, use el infame decltype(auto)en C ++ 14, que usa las decltypereglas de deducción de tipos.

Entonces, en pocas palabras, si desea copias, use auto, si desea referencias, use auto&. Úselo constsiempre que lo desee const.


EDITAR Hay un caso de uso adicional,

auto&& x = expression;

que utiliza las reglas de contracción de referencias, al igual que en el caso de reenviar referencias en el código de plantilla. Si expressiones un lvalue, entonces xes una referencia de lvalue con los calificadores cv de expression. Si expressiones un rvalue, entonces xes una referencia de rvalue.

vsoftco
fuente
@Potatoswatter Gracias, editado. De hecho, me pregunto por el cv rvalues. ¿Existe alguna forma sencilla de realizar la prueba? ¿Y cómo se puede tener un rvalue calificado de cv? En la función return cv se descarta.
vsoftco
No, no se descarta en los retornos de funciones. Se descarta para valores de todos los tipos escalares (C ++ 14 [y otras ediciones] §5 / 6). Puede obtener uno usando una llamada a función o notación de conversión. Los valores x calificados por CV funcionan igual que cualquier otra cosa: auto && x = std::move< const int >( 5 );declararán int const && x, aunque rara vez se utilizan.
Potatoswatter
@Potatoswatter gracias, tienes razón, mi error. Primero probé con POD, en cuyo caso se descarta el cv, y pensé que ese era el caso general. Por supuesto, no debe descartarse, ya que en general uno puede querer preservar el cv (por ejemplo, para que no se pueda llamar a una función miembro no constante en los retornos de rvalue).
vsoftco
Descartado para tipos escalares. Los POD pueden ser clases. De todos modos, es una esquina hackish de la intersección del modelo de expresión y el modelo de objeto.
Potatoswatter
2

Al crear variables locales, ¿es correcto usar (const) auto y o auto?

Si. El auto no es más que un tipo deducido por el compilador, así que use referencias donde normalmente usaría referencias y copias locales (automáticas) donde normalmente usaría copias locales. El uso o no de una referencia es independiente de la deducción de tipo.

Donde SomeMethod () devuelve un valor no primitivo, tal vez otro tipo definido por el usuario. Tengo entendido que const auto & result es correcto ya que el resultado devuelto por SomeMethod () llamaría al constructor de copia para el tipo devuelto. Por favor, corríjame si estoy equivocado.

¿Legal? Sí, con la const. ¿Mejores prácticas? Probablemente no, no. Al menos, no con C ++ 11. Especialmente no, si el valor devuelto por SomeMethod () ya es temporal. Querrá aprender sobre la semántica de movimiento de C ++ 11, la elisión de copias y la optimización del valor de retorno: https://juanchopanzacpp.wordpress.com/2014/05/11/want-speed-dont-always-pass-by- valor/

http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=199

https://isocpp.org/wiki/faq/ctors#return-by-value-optimization

¿Qué pasa con los tipos primitivos? Supongo que const auto sum = 1 + 2; es correcto.

Sí, esto está bien.

¿Esto también se aplica al rango basado en bucles?

para (const auto & object: objects)

Sí, esto también está bien. Escribo este tipo de código en el trabajo todo el tiempo.

tweej
fuente