tipos sirven que plantillas para los funciones c++ templates c++17

sirven - plantillas en c++



¿Por qué se usa el paquete de parámetros de plantilla en un tipo de argumento de función ya que su lista de argumentos de plantilla no se puede especificar explícitamente? (5)

Tengo el siguiente fragmento de código:

template <typename, typename> struct AAA{}; template<typename ...Args> void f(AAA<Args...> *) {} int main() { f<int, int>(nullptr); }

Este código da como resultado un error de compilación. Al compilar usando g++ -std=c++1z el error se muestra de la siguiente manera:

prog.cc: In function ''int main()'': prog.cc:8:24: error: no matching function for call to ''f<int, int>(std::nullptr_t)'' f<int, int>(nullptr); ^ prog.cc:5:6: note: candidate: template<class ... Args> void f(AAA<Args ...>*) void f(AAA<Args...> *) {} ^ prog.cc:5:6: note: template argument deduction/substitution failed: prog.cc:8:24: note: mismatched types ''AAA<Args ...>*'' and ''std::nullptr_t'' f<int, int>(nullptr);

Usando clang++ -std=c++1z el error es:

prog.cc:8:5: error: no matching function for call to ''f'' f<int, int>(nullptr); ^~~~~~~~~~~ prog.cc:5:6: note: candidate template ignored: could not match ''AAA<int, int, Args...> *'' against ''nullptr_t'' void f(AAA<Args...> *) {} ^ 1 error generated.

Estoy ejecutando los anteriores en un entorno MSYS2 MinGW-w64. Mi versión de GCC es GCC 7.1.0 y mi versión de Clang es 4.0.0; la biblioteca estándar que uso tanto en GCC como en Clang es libstdc ++ incluida con mi compilador GCC.

En mi opinión, la llamada a la plantilla de función foo tiene su parámetro de plantilla explícitamente especificado, por lo que el paquete de parámetros de la plantilla y el tipo de argumento de la función ya deberían estar especificados. Sin embargo, los diagnósticos de error que se muestran arriba parecen sugerir que el tipo exacto de parámetro de función y el argumento nullptr no coinciden, lo que parece ser un problema solo posible cuando se produce la deducción de argumento de función. Entonces mi pregunta es, ¿por qué ocurre ese error? ¿Es solo un error del compilador, o el estándar C ++ tiene algunas reglas que indican que el código original está mal formado?


Para usted está usando typename ...Args , el compilador no sabe si int, int son todos los parámetros de la plantilla en uso o más están disponibles deduciendo el argumento de la función. Por lo tanto, la función aún no se ha instanciado y el compilador sigue intentando deducir todos los demás parámetros posibles para el paquete de parámetros a partir de los argumentos de la función.

En otros términos, esto funciona:

f<int>(new AAA<int, int>);

Como está diciendo que el primer parámetro es int , pero el compilador espera una lista de parámetros y continúa intentando encontrar más y más parámetros codiciosamente desde el argumento de la función, crea una instancia de la plantilla de función.

Más o menos sucede lo mismo en su caso, pero el compilador no puede deducir nada de nullptr_t porque los argumentos de la función no coinciden. Espera un puntero a A<...> , ese no es el caso cuando pasas nullptr .
Esto funcionaría en su lugar:

template <typename, typename> struct AAA{}; template<typename A, typename B> void f(AAA<A, B> *) {} int main() { f<int, int>(nullptr); }

Como el compilador sabe que los argumentos de la plantilla son dos y usted los está proporcionando a todos, no hay nada que deducir y la función puede crearse una instancia. También tiene mucho más sentido, ya que AAA acepta solo dos parámetros de plantilla, por lo que un paquete de parámetros para f parece inútil.


Puede pensar que el compilador debe deducir el paquete como int ,int , pero el estándar de C ++ requiere explícitamente el comportamiento que observó.

[temp.arg.explicit/9]

La deducción de argumento de plantilla puede extender la secuencia de argumentos de plantilla correspondientes a un paquete de parámetros de plantilla, incluso cuando la secuencia contiene argumentos de plantilla explícitamente especificados. [Ejemplo:

template<class ... Types> void f(Types ... values); void g() { f<int*, float*>(0, 0, 0); // Types is deduced to the sequence int*, float*, int }

- ejemplo final]

Lo anterior significa que, aunque se especificaron algunos de los parámetros, la deducción no finaliza. El paquete de parámetros siempre debe ser ampliable mediante deducción de argumento. Es como si los argumentos explícitos que se dieron fueran una instanciación de una plantilla con un paquete de parámetros final. Cuando se combina con lo siguiente:

[temp.arg.explicit/3]

Los argumentos de plantilla final que se pueden deducir u obtener de los argumentos de plantilla predeterminados se pueden omitir de la lista de argumentos de plantilla explícitos. Un paquete de parámetros de plantilla final no deducido de otra manera se deducirá a una secuencia vacía de argumentos de plantilla. ...

El compilador debe hacer coincidir los argumentos no deducidos con un paquete vacío. Pero no tiene nada de lo que deducir.

Como tal, tu intento de conectar Args... en AAA no puede coincidir. Porque la secuencia de tipo es de dos tipos con una lista final (que el compilador no puede deducir como vacía de nullptr ). Mientras que AAA espera solo dos tipos.


Quiero agregar otra solución que invoque la noción {}

template <typename, typename> struct AAA{}; template<typename ...Args> void f(AAA<Args...> *) {} int main() { f<int, int>({}); }

Cuando el argumento es {} , la deducción para el parámetro está deshabilitada (contexto no deducido), por lo que no habrá desajuste y la inicialización del parámetro realmente también produce un puntero nulo.


Solo para agregar una solución fácil:

f<int, int>(nullptr); // doesn''t work for the reasons explained by other answers (*f<int, int>)(nullptr); // OK - does what you want

Este último fuerza al paquete Args... a ser {int, int} , y ahora la llamada en sí misma no es una llamada a una plantilla de función, es solo una llamada a un puntero de función. Estamos llamando a una función que toma un AAA<int, int>* y, por supuesto, pasar en nullptr es aceptable allí.

Para divertirse, también puedes agregar arbitrariamente muchos * s:

(*****f<int, int>)(nullptr); // still OK - does what you want

... pero, ya sabes ... no.


Gran respuesta de @skypjack .

Necesitas ayudar al compilador a deducir el argumento de la función:

AAA<int, int> *a = nullptr; f<int, int>(a); //works f<int, int>( (AAA<int, int> *)nullptr ); //even this will work.

Fundamentalmente, nullptr representa un "objeto sin objeto" que se puede asignar a cualquier tipo de puntero.