¿Por qué use = para inicializar un tipo primitivo en C ++?

votos
8

Donde trabajo, la mayoría de las personas piensan que los objetos se inicializan mejor usando la construcción de estilo C ++ (con paréntesis), mientras que los tipos primitivos se deben inicializar con el operador =

std::string strFoo( Foo );
int nBar = 5;

Sin embargo, nadie parece ser capaz de explicar por qué prefieren las cosas de esta manera. Puedo ver que std::string = Foo;sería ineficiente porque implicaría una copia adicional, pero ¿qué hay de malo en simplemente desterrar al =operador y usar paréntesis en todas partes?

¿Es una convención común? ¿Cuál es el pensamiento detrás de esto?

Publicado el 09/12/2008 a las 18:52
fuente por usuario
En otros idiomas...                            


9 respuestas

votos
17

Inicializar variables con el operador = o con una llamada de constructor son semánticamente iguales, solo es cuestión de estilo. Prefiero el operador =, ya que se lee de forma más natural.

Usar el operador = generalmente no genera una copia adicional; simplemente llama al constructor normal. Tenga en cuenta, sin embargo, que con tipos no primitivos, esto es solo para inicializaciones que ocurren al mismo tiempo que las declaraciones. Comparar:

std::string strFooA("Foo");  // Calls std::string(const char*) constructor
std::string strFoo = "Foo";  // Calls std::string(const char*) constructor
                             // This is a valid (and standard) compiler optimization.

std::string strFoo;  // Calls std::string() default constructor
strFoo = "Foo";      // Calls std::string::operator = (const char*)

Cuando tiene constructores predeterminados no triviales, la última construcción puede ser un poco más ineficiente.

El estándar C ++ , sección 8.5, párrafo 14 establece:

De lo contrario (es decir, para los casos de inicialización de copia restantes), se crea un temporal. Las secuencias de conversión definidas por el usuario que pueden convertir desde el tipo de fuente al tipo de destino o una clase derivada de las mismas se enumeran (13.3.1.4), y la mejor se elige a través de la resolución de sobrecarga (13.3). La conversión definida por el usuario así seleccionada se llama para convertir la expresión del inicializador en una temporal, cuyo tipo es el tipo devuelto por la llamada de la función de conversión definida por el usuario, con los cv-calificadores del tipo de destino. Si la conversión no se puede hacer o es ambigua, la inicialización está mal formada. El objeto que se inicializa se inicializa directamente desde el temporal de acuerdo con las reglas anteriores. 87 )En ciertos casos, se permite que una implementación elimine el temporal inicializando el objeto directamente; ver 12.2.

Parte de la sección 12.2 establece:

Incluso cuando se evita la creación del objeto temporal, todas las restricciones semánticas deben respetarse como si se hubiera creado el objeto temporal. [Ejemplo: incluso si no se llama al constructor de copia, se cumplirán todas las restricciones semánticas, como la accesibilidad (11). ]

Respondida el 09/12/2008 a las 18:57
fuente por usuario

votos
11

Solo sentí la necesidad de otra publicación tonta.

string str1 = "foo";

se llama copia-inicialización , porque lo que hace el compilador, si no elide ningún temporario, es:

string str1(string("foo")); 

además de verificar que el constructor de conversión utilizado sea implícito. De hecho, todas las conversiones implícitas están definidas por el estándar en términos de inicialización de copia. Se dice que una conversión implícita de tipo U a tipo T es válida, si

T t = u; // u of type U

es válida.

En contraste,

string str1("foo");

está haciendo exactamente lo que está escrito, y se llama inicialización directa . También funciona con constructores explícitos.

Por cierto, puedes desactivar elid de temporarios mediante el uso de -fno-elide-constructors:

-fno-elide-constructors
    The C++ standard allows an implementation to omit creating a temporary which 
    is only used to initialize another object of the same type. Specifying this 
    option disables that optimization, and forces G++ to call the copy constructor 
    in all cases.

El estándar dice que prácticamente no hay diferencia entre

T a = u;

y

T a(u);

si T y el tipo de u son tipos primitivos. Entonces puedes usar ambos formularios. Creo que es solo el estilo lo que hace que las personas usen la primera forma en lugar de la segunda.


Algunas personas pueden usar la primera en alguna situación, porque quieren eliminar la ambigüedad de la declaración:

T u(v(a));

Mirar a alguien como una definición de una variable uque se inicializa usando un temporal de un tipo vque recibe un parámetro para su constructor llamado a. Pero, de hecho, lo que el compilador hace con eso es esto:

T u(v a);

Crea una declaración de función que toma un argumento de tipo v, y con un parámetro llamado a. Entonces la gente lo hace

T u = v(a);

para desambiguar que, a pesar de que podrían haber hecho

T u((v(a)));

también, porque nunca hay paréntesis alrededor de los parámetros de la función, el compilador lo leería como una definición de variable en lugar de una declaración de función también :)

Respondida el 09/12/2008 a las 19:21
fuente por usuario

votos
4

A menos que haya demostrado que es importante con respecto al rendimiento, no me preocuparía una copia adicional utilizando el operador de asignación en su ejemplo ( std::string foo = "Foo";). Me sorprendería mucho que esa copia incluso exista una vez que mires el código optimizado, creo que realmente llamará al constructor parametrizado apropiado.

En respuesta a su pregunta, sí, diría que es una convención bastante común. Clásicamente, las personas han usado la asignación para inicializar tipos incorporados, y no hay una razón convincente para cambiar la tradición. La legibilidad y el hábito son razones perfectamente válidas para esta convención dado el poco impacto que tiene en el código definitivo.

Respondida el 09/12/2008 a las 18:59
fuente por usuario

votos
2

Probablemente encontrará ese código tal como

std::string strFoo = "Foo";

evitará hacer una copia adicional y compilará el mismo código (una llamada de un constructor de argumento único) como el que tiene paréntesis.

Por otro lado, hay casos en los que uno debe usar paréntesis, como una lista de inicialización de miembro constructor.

Creo que el uso de = o paréntesis para construir variables locales es en gran medida una cuestión de elección personal.

Respondida el 09/12/2008 a las 18:57
fuente por usuario

votos
1

Bueno, quién sabe lo que piensan, pero también prefiero el = para los tipos primitivos, principalmente porque no son objetos, y porque esa es la forma "habitual" de inicializarlos.

Respondida el 09/12/2008 a las 18:58
fuente por usuario

votos
0

Pero luego, para confundirte aún más, inicializas las primitivas en la lista de inicialización usando la sintaxis del objeto.

foo::foo()   
  ,anInt(0)   
  ,aFloat(0.0)   
{   
}   
Respondida el 09/12/2008 a las 19:44
fuente por usuario

votos
0

Un argumento que uno podría hacer:

std :: string foo ("barra");

Es que mantiene las cosas iguales aunque cambie el número de argumentos, es decir:

std :: string foo ("barra", 5);

No funciona con un signo '='.

Otra cosa es que para muchos objetos, un '=' no es natural, por ejemplo decir que tienes una clase Array donde el argumento da la longitud:

Array arr = 5;

No se siente bien, ya que no construimos una matriz con el valor 5, pero con una longitud 5:

Array arr (5);

se siente más natural, ya que está construyendo un objeto con el parámetro dado, no solo copiando un valor.

Respondida el 09/12/2008 a las 19:36
fuente por usuario

votos
0

Creo que es más un hábito, muy pocos objetos podrían inicializarse usando =, la cadena es uno de ellos. También es una forma de hacer lo que dijiste "usando paréntesis en todas partes (que el idioma te permite usarlo)"

Respondida el 09/12/2008 a las 18:59
fuente por usuario

votos
0

Es un problema de estilo. Incluso la afirmación de que "std :: string =" Foo "; sería ineficaz porque implicaría una copia adicional" no es correcta. Esta "copia extra" es eliminada por el compilador.

Respondida el 09/12/2008 a las 18:57
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more