c++ c++11 initializer-list overload-resolution

c++ - Resolución de sobrecarga con un inicializador de refuerzo vacío: ¿puntero o referencia?



c++ initialization list (1)

Oh, esto es desagradable.

Por [over.ics.list] p4 y p7:

4 De lo contrario, si el parámetro es una clase X no agregada y la resolución de sobrecarga por 13.3.1.7 elige un mejor constructor de X para realizar la inicialización de un objeto de tipo X de la lista de inicializador de argumentos, la secuencia de conversión implícita es un usuario -definición de la secuencia de conversión con la segunda secuencia de conversión estándar y una conversión de identidad. [...]

[...]

6 De lo contrario, si el parámetro es una referencia, consulte 13.3.3.1.4. [ Nota: Las reglas en esta sección se aplicarán para inicializar el temporal subyacente para la referencia. - nota final ] [...]

[...]

7 De lo contrario, si el tipo de parámetro no es una clase:

[...]

(7.2): si la lista de inicializadores no tiene elementos, la secuencia de conversión implícita es la conversión de identidad. [...]

La construcción de una const std::pair<int,int> temporal desde {} se considera una conversión definida por el usuario. La construcción de const std::pair<int,int> * prvalue, o const int * prvalue, o const int tempor temporal, se consideran conversiones estándar.

Se prefieren las conversiones estándar a las conversiones definidas por el usuario.

Su propio hallazgo del open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1536 de open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1536 es relevante, pero principalmente para abogados de idiomas. Es una brecha en la redacción, donde el estándar no dice realmente qué sucede con la inicialización de un parámetro de referencia desde {} , ya que {} no es una expresión. No es lo que hace que una llamada sea ambigua y la otra no, y las implementaciones están logrando aplicar el sentido común aquí.

Esta pregunta ya tiene una respuesta aquí:

Me encontré con un momento WTF de la vida real cuando descubrí que el código que aparece a continuación genera un "puntero".

#include <iostream> #include <utility> template<typename T> struct bla { static void f(const T*) { std::cout << "pointer/n"; } static void f(const T&) { std::cout << "reference/n"; } }; int main() { bla<std::pair<int,int>>::f({}); }

Al cambiar el argumento de la plantilla std::pair<int,int> por un tipo int o cualquier otro tipo primitivo, aparece el error (para mí al menos) esperado "sobrecarga ambigua". Parece que los tipos incorporados son especiales aquí, porque cualquier tipo definido por el usuario (agregado, no trivial, con el constructor predeterminado, etc.) lleva a que se llame a la sobrecarga del puntero. Creo que la plantilla no es necesaria para reproducirla, solo hace que sea sencillo probar diferentes tipos.

Personalmente, no creo que sea lógico y esperaría el error de sobrecarga ambiguo en todos los casos, independientemente del argumento de la plantilla. GCC y Clang (y creo que MSVC) no están de acuerdo conmigo, en C ++ 11/14 / 1z. Tenga en cuenta que soy plenamente consciente de la mala API que presentan estas dos sobrecargas, y nunca escribiría algo así, lo prometo .

Entonces la pregunta es: ¿qué está pasando?