c++ - ¿Por qué las deducciones de tipo de plantilla y auto difieren para los inicializadores reforzados?




c++11 templates (2)

Entiendo que, dado un inicializador reforzado, el auto deducirá un tipo de std::initializer_list , mientras que la deducción del tipo de plantilla fallará:

auto var = { 1, 2, 3 };   // type deduced as std::initializer_list<int>

template<class T> void f(T parameter);

f({ 1, 2, 3 });          // doesn't compile; type deduction fails

Incluso sé dónde se especifica esto en el estándar C ++ 11: 14.8.2.5/5 bullet 5:

[Es un contexto no deducido si el programa tiene] Un parámetro de función para el cual el argumento asociado es una lista de inicializadores (8.5.4) pero el parámetro no tiene std :: initializer_list o referencia a std calificado como cv-initializer_list tipo. [ Ejemplo:

plantilla void g (T);

g ({1,2,3}); // error: no se deduce argumento para T

- ejemplo final ]

Lo que no sé o entiendo es por qué existe esta diferencia en el comportamiento de deducción de tipo. La especificación en el CD de C ++ 14 es la misma que en C ++ 11, por lo que es de suponer que el comité de estandarización no considera el comportamiento de C ++ 11 como un defecto.

¿Alguien sabe por qué el auto deduce un tipo para un inicializador reforzado, pero las plantillas no están permitidas? Si bien las explicaciones especulativas de la forma "esta podría ser la razón" son interesantes, me interesan especialmente las explicaciones de las personas que saben por qué el estándar fue escrito de la manera en que lo fue.


En primer lugar, se trata de "explicaciones especulativas de la forma" este podría ser el motivo "", como usted lo llama.

{1,2,3} no es solo std::initializer_list<int> sino que también permite inicializar tipos sin constructor. Por ejemplo:

#include <initializer_list>

struct x{
    int a,b,c;
};

void f(x){

}
int main() {
    f({1,2,3});
}

es el codigo correcto Para mostrar que no es initializer_list veamos el siguiente código:

#include <initializer_list>

struct x{int a,b,c;};

void f(x){

}
int main() {
    auto il = {1, 2, 3};
    f(il);
}

Error es:

prog.cpp: In function int main()’:
prog.cpp:10:9: error: could not convert il from std::initializer_list<int> to x

Y ahora a la pregunta "¿Cuál es la diferencia?"

en auto x = {1, 2, 3}; el código está bien para determinar el tipo, porque el codificador dijo explícitamente "No es importante qué tipo es" usando el auto

Mientras que en el caso de la plantilla de función, puede estar seguro de que está usando un tipo diferente. Y es bueno prevenir errores en casos ambiguos (no parece un estilo C ++, a través de).

Especialmente malo será en el caso de que haya una función f(x) y luego se cambie a la plantilla uno. El programador escribió para usarlo como x , y después de agregar una nueva función para otro tipo, cambia ligeramente para llamar a una completamente diferente.


Hay dos razones importantes para que las plantillas no hagan ninguna deducción (las dos que recuerdo en una discusión con el responsable)

  • Preocupaciones sobre futuras extensiones de lenguaje (hay varios significados que podrías inventar, ¿qué tal si quisiéramos introducir un reenvío perfecto para los argumentos de la función de lista de iniciación?)

  • Las llaves a veces pueden inicializar válidamente un parámetro de función que es dependiente

template<typename T>
void assign(T &d, const T& s);
int main() {
  vector<int> v;
  assign(v, { 1, 2, 3 });
}

Si T se deduciría en el lado derecho de initializer_list<int> pero en el lado izquierdo de vector<int> , esto no funcionaría debido a una deducción de argumento contradictorio.

La deducción para auto to initializer_list<T> es controvertida. Existe una propuesta para C ++ - after-14 para eliminarlo (y para prohibir la inicialización con { } o {a, b} , y para hacer {a} deducción al tipo de a ).







list-initialization