me he tropezado”¿Por qué la deducción del argumento de la plantilla no funciona aquí?” recientemente y las respuestas se pueden resumir en “Es un contexto no deducido”.
Específicamente, el primero dice que es tal cosa y luego redirige al estándar para “detalles”, mientras que el segundo cita el estándar, que es críptico por decir lo menos.
¿Puede alguien por favor explicar a los simples mortales, como yo, qué contexto no deducido es, ¿cuándo ocurre y por qué ocurre?
Deducción se refiere al proceso de determinar el tipo de un parámetro de plantilla a partir de un argumento dado. Se aplica a las plantillas de funciones, auto
, y algunos otros casos (por ejemplo, especialización parcial). Por ejemplo, considere:
template <typename T> void f(std::vector<T>);
Ahora si dices f(x)
donde declaraste std::vector<int> x;
luego T
es deducido como int
y obtienes la especialización f<int>
.
Para que la deducción funcione, el tipo de parámetro de plantilla que se va a deducir tiene que aparecer en un contexto deducible. En este ejemplo, el parámetro de función de f
es un contexto tan deducible. Es decir, un argumento en la expresión de la llamada a la función nos permite determinar cuál es el parámetro de la plantilla. T
debe ser para que la expresión de llamada sea válida.
Sin embargo, también hay no-Contextos deducidos, donde no es posible la deducción. El ejemplo canónico es “un parámetro de plantilla que aparece a la izquierda de un ::
:
template <typename> struct Foo;
template <typename T> void g(typename Foo<T>::type);
En esta plantilla de función, el T
en la lista de parámetros de la función está en un contexto no deducido. Así no puedes decir g(x)
y deducir T
. La razón de esto es que no existe una “correspondencia hacia atrás” entre tipos arbitrarios y miembros Foo<T>::type
. Por ejemplo, podrías tener especializaciones:
template <> struct Foo<int> { using type = double; };
template <> struct Foo<char> { using type = double; };
template <> struct Foo<float> { using type = bool; };
template <> struct Foo<long> { int type = 10; };
template <> struct Foo<unsigned> { };
si llamas g(double{})
hay dos respuestas posibles para T
y si llamas g(int{})
no hay respuesta. En general, no existe una relación entre los parámetros de la plantilla de clase y los miembros de la clase, por lo que no puede realizar ninguna deducción de argumentos sensata.
Ocasionalmente, es útil inhibir la deducción de argumentos explícitamente. Este es por ejemplo el caso de std::forward
. Otro ejemplo es cuando tienes conversiones de Foo<U>
para Foo<T>
digamos, u otras conversiones (piense std::string
y char const *
). Ahora suponga que tiene una función libre:
template <typename T> bool binary_function(Foo<T> lhs, Foo<T> rhs);
si llamas binary_function(t, u)
, entonces la deducción puede ser ambigua y por lo tanto fallar. Pero es razonable deducir sólo un argumento y no deducir el otro, permitiendo así conversiones implícitas. Ahora se necesita un contexto explícitamente no deducido, por ejemplo como este:
template <typename T>
struct type_identity {
using type = T;
};
template <typename T>
bool binary_function(Foo<T> lhs, typename type_identity<Foo<T>>::type rhs)
{
return binary_function(lhs, rhs);
}
(Es posible que haya experimentado tales problemas de deducción con algo como std::min(1U, 2L)
.)
Relacionado: C++, el argumento de la plantilla no se puede deducir
– Shafik Yaghmour
11 de agosto de 2014 a las 14:22