c++ - Error de sustitución con `std:: function` y el parámetro de plantilla deducido previamente-¿por qué?
c++11 templates (2)
Considere el siguiente código:
template <typename>
struct S { };
void g(S<int> t);
template <typename T>
void f(T, std::function<void(S<T>)>);
Al intentar invocar
f(0, g);
Obtuve el siguiente error:
error: no matching function for call to ''f'' f(0, g); ^ note: candidate template ignored: could not match ''function<void (S<type-parameter-0-0>)>'' against ''void (*)(S<int>)'' void f(T, std::function<void(S<T>)>); ^
ejemplo en vivo en godbolt.org
Si bien entiendo que, en general, el tipo del parámetro
std::function
no se puede deducir, ya que es un contexto no deducido
En este caso,
T
puede deducirse primero mediante el argumento
0
pasado, y luego sustituirse en
std::function<void(S<T>)>
para obtener
std::function<void(S<int>)>
.
Esperaría que después de deducir
T=int
, el compilador sustituya
T
en todas partes en la firma y luego intente construir el parámetro
std::function
con el argumento
g
.
¿Por qué ese no es el caso? Supongo que el orden en que ocurre la sustitución / deducción tiene algo que ver con esto, pero me gustaría ver la redacción estándar relevante.
Pregunta adicional: ¿ es esto algo que podría modificarse en un futuro estándar mientras se preserva la compatibilidad con versiones anteriores, o hay una razón fundamental por la que este tipo de sustitución no funciona?
Si bien entiendo que, en general, el tipo del parámetro
std::function
no se puede deducir, ya que es un contexto no deducido, en este caso,T
se puede deducir primero con el argumento pasado0
.
Esto no es verdad.
T
es deducible en este contexto.
Si cambia el código a
template <typename T>
void f(std::function<void(S<T>)>);
int main()
{
f(std::function<void(S<int>)>(g));
}
el código se compilaría y
T
se deduciría correctamente.
Su problema es que está pasando un objeto a la función de la que no puede extraer
T
El compilador no realizará ninguna conversión de los argumentos de la función cuando intente deducir
T
Eso significa que tiene un
int
y una función como los tipos pasados a la función.
Obtiene
int
desde
0
, luego intenta obtener el tipo de la
std::function
que pasa en el segundo parámetro, pero como no pasó una
std::function
no puede extraer
T
y por eso, obtiene un error.
Si bien entiendo que, en general, el tipo del parámetro std :: function no se puede deducir, ya que es un contexto no deducido
No es un contexto no deducido.
Todo lo contrario.
Debido a que se intenta la deducción para el parámetro
std::funcition
, pero el argumento
no
es una
std::function
, la deducción falla.
La deducción de argumentos de plantilla a partir de argumentos de función debe estar de acuerdo con todos los argumentos de función.
Si falla por uno, falla por completo.
[temp.deduct.type]
2 En algunos casos, la deducción se realiza utilizando un solo conjunto de tipos P y A, en otros casos, habrá un conjunto de tipos correspondientes P y A. La deducción de tipo se realiza de forma independiente para cada par P / A, y el deducido Los valores de los argumentos de plantilla se combinan. Si no se puede realizar la deducción de tipo para cualquier par P / A, o si para cualquier par la deducción conduce a más de un conjunto posible de valores deducidos, o si diferentes pares producen valores deducidos diferentes, o si algún argumento de plantilla permanece ni deducido ni explícitamente especificado, la deducción de argumento de plantilla falla.
Convertir el tipo del segundo parámetro de función en un contexto no deducido es en realidad cómo se puede superar el error:
#include <functional>
template<typename T>
struct type_identity {
using type = T;
};
template <typename>
struct S { };
void g(S<int> ) {}
template <typename T>
void f(T, typename type_identity<std::function<void(S<T>)>>::type) {}
int main() {
f(0, g);
}
T
se deduce con éxito del argumento de la primera función, y no queda nada por deducir.
Por lo tanto, la ejecución se considera un éxito.