Si un modèle de fonction a un type de retour déduit, existe-t-il un moyen de l'appeler sans instancier la définition?

Nov 20 2020

Considérez un modèle de fonction tel que:

template <class T>
const auto& foo() { static T t; return t; }

La définition ne serait pas valide si Tc'était le cas void. Néanmoins, nous sommes autorisés à instancier la déclaration seule sans déclencher d'erreur:

extern template const auto& foo<void>();  // explicit instantiation declaration

Considérons maintenant les situations où fooest appelé, plutôt que d'être explicitement instancié. Évidemment, si elle fooest appelée dans un contexte évalué, la définition de la spécialisation sera instanciée. Qu'en est-il dans un contexte non évalué? On sait que si un modèle de fonction avec un type de retour non déduit est appelé dans un contexte non évalué, la définition de la spécialisation n'est pas instanciée. L'exemple évident de ceci est std::declval<T>. Il n'est pas clair si la même chose est possible pour une fonction qui a un type de retour déduit.

Par exemple, j'ai considéré ceci:

static_assert(sizeof( (void)foo<void>(), char{} ) == 1);

Cependant, même dans cette situation, où le compilateur a définitivement suffisamment d'informations pour évaluer l' sizeofexpression sans connaître le type de retour, une erreur de compilation se produit toujours ( lien godbolt ).

  • Quelle disposition de la norme nécessite l'instanciation de la définition de foo<void>dans cette situation?
  • Existe-t-il un moyen qui foo<void>puisse être appelé à l'intérieur d'une expression non évaluée qui n'instancierait pas sa définition?

Réponses

3 dfrib Nov 19 2020 at 23:52

Quelle disposition de la norme nécessite l'instanciation de la définition de foo<void>dans cette situation?

Bien que non normative, la note dans [dcl.spec.auto] / 11 mentionne que toute utilisation d'une spécialisation (d'un modèle de fonction avec un espace réservé dans son type de retour déclaré) provoquera une instanciation implicite [extrait, je souligne ] :

[...] [  Note: Par conséquent, toute utilisation d'une spécialisation du modèle de fonction provoquera une instanciation implicite.

et, de plus, [dcl.spec.auto] / 14 couvre le cas particulier de permettre une déclaration d'instanciation explicite sans déclencher d'instanciation, tout en laissant peut-être aussi entendre que les mécanismes d'instanciation déclenchés pour déterminer le type de retour pour un modèle de fonction sont quelque peu séparés de les mécanismes d'instanciation "réguliers" [c'est moi qui souligne ]:

Une déclaration d'instanciation explicite ne provoque pas l'instanciation d'une entité déclarée à l'aide d'un type d'espace réservé, mais elle n'empêche pas non plus que cette entité soit instanciée selon les besoins pour déterminer son type . [  Exemple:

template <typename T> auto f(T t) { return t; }
extern template auto f(int);    // does not instantiate f<int>
int (*p)(int) = f;              // instantiates f<int> to determine its return type, but an explicit
                                // instantiation definition is still required somewhere in the program

 -  fin d'exemple  ]

où le commentaire (non normatif) à l'exemple indique qu'une telle instanciation implicite déclenchée dans un cas particulier n'est utilisée que pour la déduction de type de retour, et ne dispense pas de la nécessité d'une définition d'instanciation explicite ailleurs.


Existe-t-il un moyen qui foo<void>puisse être appelé à l'intérieur d'une expression non évaluée qui n'instancierait pas sa définition?

Compte tenu de la discussion ci-dessus, je dirais: non. L'appel même à l'intérieur d'une expression non évaluée relèverait du (non normatif) «toute utilisation de».