c++ - ¿Por qué las deducciones de tipo de plantilla y auto difieren para los inicializadores reforzados?
c++11 templates (3)
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.
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.
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
).
La razón se describe en N2640 :
Una lista {} no puede deducir contra un parámetro de tipo plano
T
Por ejemplo:
template<class T> void count(T); // (1). struct Dimensions { Dimensions(int, int); }; size_t count(Dimensions); // (2). size_t n = count({1, 2}); // Calls (2); deduction doesn''t // succeed for (1).
Otro ejemplo:
template<class T> void inc(T, int); // (1) template<class T> void inc(std::initializer_list<T>, long); // (2) inc({1, 2, 3}, 3); // Calls (2). (If deduction had succeeded // for (1), (1) would have been called — a // surprise.)
Por otro lado, ser capaz de deducir una lista de
initializer_list<X>
paraT
es atractivo para permitir:
auto x = { 1, 1, 2, 3, 5 }; f(x); g(x);
que se consideró un comportamiento deseable desde el comienzo de las discusiones del EWG sobre las listas de inicialización.
En lugar de idear una regla de deducción inteligente para un tipo de parámetro
T
coincida con una lista {} (una opción que seguimos en bocetos y borradores anteriores de este documento), ahora preferimos manejar esto con un caso especial para "auto" deducción de variables cuando el inicializador es una lista {}. Es decir, para el caso específico de una variable declarada con un especificador de tipo "auto" y un inicializador de lista {}, el "auto" se deduce como para una funciónf(initializer_list<T>)
lugar de como para una funciónf(T)
.
Para concluir, el problema es que si permitimos que una lista {} deduzca contra un parámetro T
tipo plano, entonces la función con el parámetro T
tendría una prioridad muy alta durante la resolución de sobrecarga, lo que puede causar un comportamiento cableado (como en los ejemplos anteriores) .