Pourquoi le paquet de parameters de modèle est-il utilisé dans un type d’argument de fonction en tant que liste d’arguments de modèle qui ne peut pas être explicitement spécifié?

J’ai le morceau de code suivant:

template  struct AAA{}; template void f(AAA *) {} int main() { f(nullptr); } 

Ce code entraîne une erreur de compilation. Lors de la compilation à l’aide de g++ -std=c++1z l’erreur est la suivante:

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

En utilisant clang++ -std=c++1z l’erreur est:

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

Je les exécute dans un environnement MSYS2 MinGW-w64. Ma version de GCC est GCC 7.1.0 et ma version de Clang est 4.0.0; La bibliothèque standard que j’utilise à la fois dans GCC et dans Clang est le libstdc ++ fourni avec mon compilateur GCC.

À mon avis, l’appel au modèle de fonction foo a son paramètre de modèle spécifié explicitement, ainsi le paquet de parameters de modèle et le type d’argument de fonction devraient déjà être spécifiés. Cependant, les diagnostics d’erreur présentés ci-dessus semblent suggérer que le type exact de paramètre de fonction et l’argument nullptr ne correspondent pas, ce qui semble être un problème uniquement possible lorsque la déduction d’argument de fonction se produit. Donc, ma question est la suivante: pourquoi une telle erreur se produit-elle? Est-ce juste un bogue de compilateur ou la norme C ++ contient-elle des règles qui indiquent que le code d’origine est simplement mal formé?

Vous pouvez penser que le compilateur doit déduire le pack comme int ,int , mais le standard C ++ requirejs explicitement le comportement que vous avez observé.

[temp.arg.explicit / 9]

La déduction des arguments de modèle peut étendre la séquence des arguments de modèle correspondant à un pack de parameters de modèle, même lorsque la séquence contient des arguments de modèle spécifiés explicitement. [ Exemple:

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

– exemple de fin]

Ce qui précède signifie que même si certains parameters ont été spécifiés, la déduction ne se termine pas. Le pack de parameters doit toujours pouvoir être développé par déduction d’argument. C’est comme si les arguments explicites donnés étaient une instanciation d’un modèle avec un ensemble de parameters de fin. Lorsque couplé avec les éléments suivants:

[temp.arg.explicit / 3]

Les arguments de modèle qui peuvent être déduits ou obtenus à partir des arguments de modèle par défaut peuvent être omis de la liste des arguments de modèle explicites. Un ensemble de parameters de modèle de fin non déduit sera déduit d’une séquence vide d’arguments de modèle.

Le compilateur doit faire correspondre les arguments non déduits à un pack vide. Mais ça n’a rien à en déduire.

En tant que tel, votre tentative de connecter Args... à AAA ne peut pas correspondre. Parce que la séquence de type est deux types avec une liste de fin (que le compilateur ne peut pas déduire comme vide de nullptr ). Alors que AAA attend seulement deux types.

Car vous utilisez typename ...Args , le compilateur ne sait pas si int, int sont tous les parameters de template utilisés ou plus sont disponibles en déduisant l’argument de la fonction. Par conséquent, la fonction n’est pas encore instanciée et le compilateur continue d’essayer de déduire tous les autres parameters possibles pour le paquet de parameters à partir des arguments de la fonction.

En d’autres termes, cela fonctionne:

 f(new AAA); 

Parce que vous dites que le premier paramètre est int , mais que le compilateur attend une liste de parameters et continue à essayer de trouver de plus en plus de parameters avec avidité dans l’argument de la fonction, il instancie alors le modèle de fonction.

Plus ou moins la même chose se produit dans votre cas, mais le compilateur ne peut rien déduire de nullptr_t car les arguments de fonction ne correspondent pas. Il attend un pointeur sur un A<...> , ce n’est pas le cas lorsque vous passez à nullptr .
Cela fonctionnerait plutôt:

 template  struct AAA{}; template void f(AAA *) {} int main() { f(nullptr); } 

Comme le compilateur sait que les arguments de modèle sont deux et que vous les fournissez tous, il n’y a rien à déduire et la fonction peut être instanciée. Cela a également beaucoup plus de sens, car AAA n’accepte que deux parameters de modèle, donc un paquet de parameters pour f semble inutile ici.

Juste pour append une solution simple:

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

Ce dernier force le pack Args... à être {int, int} , et l’appel lui-même n’est plus un appel à un modèle de fonction – c’est juste un appel à un pointeur de fonction. Nous appelons une fonction qui prend un AAA* , et bien sûr, le passage de nullptr est acceptable.

Pour le plaisir, vous pouvez également append arbitrairement beaucoup de * :

 (*****f)(nullptr); // still OK - does what you want 

… mais tu sais … non.

Je veux append une autre solution qui appelle la notion {}

 template  struct AAA{}; template void f(AAA *) {} int main() { f({}); } 

Lorsque l’argument est {} , la déduction pour le paramètre est désactivée (contexte non réduit), il n’y aura donc pas de discordance et l’initialisation du paramètre produit en fait un pointeur nul.

Bonne réponse de @skypjack .

Vous devez aider le compilateur à déduire l’argument de la fonction:

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

Fondamentalement, nullptr représente un “pas d’object” pouvant être affecté à n’importe quel type de pointeur.