En la era ilustrada de 2016, con dos nuevos estándares en nuestro haber desde que se hizo esta pregunta y uno nuevo a la vuelta de la esquina, lo crucial que debe saber es que los compiladores que admiten el estándar C ++ 17 compilarán su código tal como está. .
Deducción de argumento de plantilla para plantillas de clase en C ++ 17
Aquí (cortesía de una edición de Olzhas Zhumabek de la respuesta aceptada) está el documento que detalla los cambios relevantes al estándar.
Abordar preocupaciones de otras respuestas
La respuesta mejor calificada actual
Esta respuesta señala que el "constructor de copias y operator=
" no conocerían las especializaciones de plantilla correctas.
Esto es una tontería, porque el constructor de copia estándar y operator=
solo existe para un tipo de plantilla conocido :
template <typename T>
class MyClass {
MyClass(const MyClass&) =default;
... etc...
};
// usage example modified from the answer
MyClass m(string("blah blah blah"));
MyClass *pm; // WHAT IS THIS?
*pm = m;
Aquí, como señalé en los comentarios, no hay razón para MyClass *pm
ser una declaración legal con o sin la nueva forma de inferencia: MyClass
no es un tipo (es una plantilla), por lo que no tiene sentido declarar un puntero de tipo MyClass
. Aquí hay una forma posible de corregir el ejemplo:
MyClass m(string("blah blah blah"));
decltype(m) *pm; // uses type inference!
*pm = m;
Aquí, yapm
es del tipo correcto, por lo que la inferencia es trivial. Además, es imposible mezclar tipos accidentalmente al llamar al constructor de copias:
MyClass m(string("blah blah blah"));
auto pm = &(MyClass(m));
Aquí, pm
habrá un puntero a una copia de m
. Aquí, MyClass
se está construyendo una copia a partir de lo m
que es de tipo MyClass<string>
(y no del tipo inexistente MyClass
). Por lo tanto, en el punto donde pm
se infiere 's tipo, no es información suficiente para saber que el tipo de plantilla de m
, y por lo tanto el tipo de plantilla de pm
, es string
.
Además, lo siguiente siempre generará un error de compilación :
MyClass s(string("blah blah blah"));
MyClass i(3);
i = s;
Esto se debe a que la declaración del constructor de copia no tiene plantilla:
MyClass(const MyClass&);
Aquí, el tipo de plantilla del argumento del constructor de copia coincide con el tipo de plantilla de la clase en general; es decir, cuando MyClass<string>
se instancia, MyClass<string>::MyClass(const MyClass<string>&);
se instancia con él, y cuando MyClass<int>
se instancia, MyClass<int>::MyClass(const MyClass<int>&);
se instancia. A menos que se especifique explícitamente o se declare un constructor con plantilla, no hay razón para que el compilador cree una instancia MyClass<int>::MyClass(const MyClass<string>&);
, lo que obviamente sería inapropiado.
La respuesta de Cătălin Pitiș
Pitiș da un ejemplo deduciendo Variable<int>
y Variable<double>
luego dice:
Tengo el mismo nombre de tipo (Variable) en el código para dos tipos diferentes (Variable y Variable). Desde mi punto de vista subjetivo, afecta bastante la legibilidad del código.
Como se señaló en el ejemplo anterior, en Variable
sí mismo no es un nombre de tipo, aunque la nueva característica hace que parezca uno sintácticamente.
Pitiș luego pregunta qué pasaría si no se proporciona un constructor que permita la inferencia adecuada. La respuesta es que no se permite ninguna inferencia, porque la inferencia se activa mediante la llamada al constructor . Sin una llamada al constructor, no hay inferencia .
Esto es similar a preguntar qué versión de foo
se deduce aquí:
template <typename T> foo();
foo();
La respuesta es que este código es ilegal, por la razón indicada.
La respuesta de MSalter
Esta es, por lo que puedo decir, la única respuesta que plantea una preocupación legítima sobre la función propuesta.
El ejemplo es:
Variable var(num); // If equivalent to Variable<int> var(num),
Variable var2(var); // Variable<int> or Variable<Variable<int>> ?
La pregunta clave es, ¿el compilador selecciona aquí el constructor de tipo inferido o el constructor de copia ?
Al probar el código, podemos ver que el constructor de copia está seleccionado. Para ampliar el ejemplo :
Variable var(num); // infering ctor
Variable var2(var); // copy ctor
Variable var3(move(var)); // move ctor
// Variable var4(Variable(num)); // compiler error
No estoy seguro de cómo la propuesta y la nueva versión del estándar especifican esto; parece estar determinado por "guías de deducción", que son un nuevo estándar que todavía no entiendo.
Tampoco estoy seguro de por qué la var4
deducción es ilegal; el error del compilador de g ++ parece indicar que la declaración se está analizando como una declaración de función.