with initialize argument c++ c++11 initializer-list

c++ - argument - Interrupción silenciosa de las llamadas del constructor después de agregar el constructor initializer_list



c++ initializer list (3)

Consideremos lo siguiente:

#include <iostream> #include <initializer_list> class Foo { public: Foo(int) { std::cout << "with int/n"; } }; int main() { Foo a{10}; // new style initialization Foo b(20); // old style initialization }

Al ejecutarse se imprime:

with int with int

Todo bien. Ahora, debido a los nuevos requisitos, he agregado un constructor que toma una lista de inicializadores.

Foo(std::initializer_list<int>) { std::cout << "with initializer list/n"; }

Ahora se imprime:

with initializer list with int

Así que mi viejo código Foo a{10} se rompió silenciosamente. Se suponía que se inicializaba con un int .

Entiendo que la sintaxis del idioma está considerando {10} como una lista con un elemento. Pero, ¿cómo puedo evitar la ruptura silenciosa de código antiguo?

  1. ¿Hay alguna opción de compilador que nos avise en tales casos? Dado que esto será específico del compilador, estoy más interesado en gcc. Ya he intentado -Wall -Wextra .
  2. Si no existe tal opción, ¿siempre debemos usar la construcción de estilo antiguo, es decir, con () Foo b(20) , para otros constructores y usar {} solo cuando realmente nos referimos a una lista de inicializadores?

Es imposible generar una advertencia en estos casos, porque el comportamiento presentado al elegir el constructor std::initializer_list en lugar de las coincidencias directas está bien definido y es compatible con un estándar.

Este problema se describe en detalle en el artículo 7 de Scott Meyers Effective Modern C ++ Modern :

Sin embargo, si uno o más constructores declaran un parámetro de tipo std::initializer_list , las llamadas que usan la sintaxis de inicialización reforzada prefieren las sobrecargas que toman std :: initializer_lists. Fuertemente. Si hay alguna forma en que los compiladores puedan interpretar una llamada utilizando un inicializador reforzado para que sea un constructor que toma una lista std::initializer_list , los compiladores emplearán esa interpretación.

También presenta algunos de los casos más recientes de este problema, recomiendo encarecidamente que lo lea.


No hay advertencias del compilador y nunca las habrá. Simplemente no tiene sentido advertir en el código haciendo algo común como

std::vector vec{1};

Recuerde que el compilador solo advierte sobre cosas realmente no deseadas, como un comportamiento indefinido. No tiene forma de saber que en la definición anterior, querías llamar al constructor tomando un argumento de tamaño. Por lo que sabe, ¡realmente quieres tener un vector con un elemento! No puede leer tu mente :)

La respuesta a tu segunda pregunta es básicamente sí. Siempre puedes agregar un parámetro ficticio como struct {} dummy; para evitar usar el constructor con la lista de inicializadores, pero en realidad, la única solución es usar paréntesis en lugar de llaves (o no romper la interfaz de repente).

Si desea cambiar cada parte del código que usa la inicialización de la lista, puede eliminar el constructor de la lista de inicializadores, cambiarlos a llaves y luego implementar el constructor correctamente. Considero que tal cambio es una ruptura y lo trataré de manera apropiada. La otra idea hubiera sido presentar el caso de uso de la lista de inicializadores de antemano e implementarlo de inmediato.


No pude encontrar una opción así, por lo que aparentemente, en tales casos, debería usar paréntesis para las clases que tienen el constructor initializer_list, y una inicialización uniforme para todas las demás clases que desee.

Se pueden encontrar algunas ideas útiles en esta respuesta y comentarios: https://.com/a/18224556/2968646