Considera este código:
#include <vector>
#include <iostream>
enum class A
{
X, Y
};
struct Test
{
Test(const std::vector<double>&, const std::vector<int>& = {}, A = A::X)
{ std::cout << "vector overload" << std::endl; }
Test(const std::vector<double>&, int, A = A::X)
{ std::cout << "int overload" << std::endl; }
};
int main()
{
std::vector<double> v;
Test t1(v);
Test t2(v, {}, A::X);
}
Esto imprime:
vector overload
int overload
¿Por qué esto no produce un error de compilación debido a una resolución de sobrecarga ambigua? Si se elimina el segundo constructor, obtenemos vector overload
dos veces. ¿Cómo / por qué métrica es int
una combinación inequívocamente mejor {}
que std::vector<int>
?
La firma del constructor seguramente se puede recortar aún más, pero acabo de engañarme con un código equivalente y quiero asegurarme de que no se pierda nada importante para esta pregunta.
c++
language-lawyer
overload-resolution
Max Langhof
fuente
fuente
{}
como un bloque de código, asigna 0 a las variables - ejemplo: const char x = {}; se establece en 0 (null char), lo mismo para int etc.{}
efectivamente hace en ciertos casos especiales, pero generalmente no es correcto (para empezar,std::vector<int> x = {};
funciona,std::vector <int> x = 0;
no lo hace). No es tan simple como "{}
asigna cero".struct A { int x = 5; }; A a = {};
no asigna cero en ningún sentido, construye unA
cona.x = 5
. Esto es diferenteA a = { 0 };
, que se inicializaa.x
a 0. El cero no es inherente a{}
, es inherente a cómo cada tipo se construye por defecto o se inicializa en valor. Mira aquí , aquí y aquí .Respuestas:
Está en [over.ics.list] , énfasis mío
El
std::vector
constructor lo inicializa y la viñeta en negrita lo considera una conversión definida por el usuario. Mientras tanto, para unaint
, esta es la conversión de identidad, por lo que supera el rango del primer c'tor.fuente
0
tiene el tipoint
pero no el tipostd::vector<int>
, ¿cómo es eso un "como si" fuera de la naturaleza "sin tipo"{}
?std::vector<int>
. Como dijiste, esperaba que "el tipo de parámetro termine decidiendo cuál es el tipo de argumento", y un{}
"tipo" (por así decirlo)std::vector<int>
no debería necesitar una conversión (no de identidad) para inicializar astd::vector<int>
. El estándar obviamente dice que sí, así que eso es todo, pero no tiene sentido para mí. (Eso sí, no estoy argumentando que usted o el estándar están equivocados, solo estoy tratando de conciliar esto con mis modelos mentales.)