4
votes

gcc vs clang: noexcept analysé dans la spécialisation de modèle inutilisé lors de la diffusion statique

J'essaie de convertir en statique un pointeur de fonction vers une surcharge de fonction spécifique, mais il semble que clang analyse toujours l'instruction noexcept de la spécialisation de modèle (inutilisé) et génère ainsi une erreur du compilateur. GCC ne semble pas se soucier du no, sauf si la surcharge de fonction correspondante est inutilisée.

template<typename T>
void fun( T ) noexcept( T(1) ){}

void fun(int) {}

void fun(int*) {}

int main () {
    int a;
    fun(&a); //calling works fine
    fun(a);
    static_cast<void(*)(int*)>(&fun); // static casting doesn't
}

https://godbolt.org/z/ixpl3f

Quel compilateur ne va pas ici?
La norme spécifie-t-elle exactement ce qui doit être compilé lors du transtypage de pointeurs de fonction vers des surcharges spécifiques?

EDIT: Après le commentaire de Maxim, j'ai remis le deuxième noexcept dans l'exemple et il a été compilé à la fois sur gcc et clang.

Voici donc un autre exemple, où il échoue en fait avec noexcept (noexcept (... ))

https://godbolt.org/z/NMW99C a>


0 commentaires

3 Réponses :


2
votes

Quand il s'agit de prendre l'adresse d'un modèle de fonction, comme le fait votre distribution statique, la norme a ce paragraphe pertinent:

[over.over] Adresse de la fonction surchargée (c'est moi qui souligne)

2 Si le nom est un modèle de fonction, la déduction d'argument de modèle est effectuée ([temp.deduct.funcaddr]), et si la déduction d'argument réussit, la liste d'arguments de modèle résultante est utilisée pour générer une spécialisation de modèle de fonction unique , qui est ajoutée à l'ensemble des fonctions surchargées considérées.

La déduction des arguments de modèle (qui ne prend pas en compte la spécification d'exception), réussit. Ainsi, la fonction entière est instanciée. Ce n'est qu'après cette instanciation que le compilateur vérifiera s'il existe une meilleure correspondance non-modèle:

4 Si plusieurs fonctions sont sélectionnées, toute spécialisation de modèle de fonction dans l'ensemble est éliminée si l'ensemble contient également une fonction qui n'est pas une spécialisation de modèle de fonction

Ceci est différent du cas d'un appel de fonction, où la résolution de surcharge supprimera la spécialisation de fonction tôt en fonction de l'existence d'une surcharge non-modèle. Dans ce cas, la norme requiert l'existence de la fonction lvalue avant d'être certain qu'elle est vraiment nécessaire. Je ne sais pas si cela peut être considéré comme un défaut de formulation ou s'il existe une cause plus élevée, mais cela ne semble pas intuitif.

Donc, la conclusion est que Clang n'a pas tort, mais GCC est beaucoup plus intuitif dans son comportement.


3 commentaires

J'ai regardé de plus près mon code et je ne crée pas de spécialisations de modèles, car j'ai oublié de mettre template<> là-bas. Et si je le fais, les deux compilateurs se plaignent de spécifications noexcept différentes. Donc pas sûr de ce qui devrait s'appliquer ici. probablement plutôt des règles pour la résolution de surcharge de fonction


@kawillzocken - Les spécialisations seraient une toute autre boîte de vers. Je vous recommande de vous en tenir à la surcharge.


Ma réponse précédente a manqué le point de la question, je l'ai donc supprimée. Ma nouvelle réponse est beaucoup plus proche de la vôtre.



0
votes

Un peu hors sujet, mais il convient de mentionner que si vous souhaitez dire que le modèle de fonction fun est noexcept uniquement si T (1) ne lance pas, alors

template<typename T>
void fun( T ) noexcept(noexcept(T(1))){}

Devrait être

template<typename T>
void fun( T ) noexcept( T(1) ){}

Le premier noexcept est un spécificateur , le second est un opérateur .


1 commentaires

sûr! j'ai supprimé le second no, sauf pour le rendre plus minimal: D bien qu'il défait évidemment la sémantique



1
votes

[over.over] ¶1 Une fonction de type F est sélectionnée pour le type de fonction FT du type de cible requis dans le contexte si F (après éventuellement appliquer la conversion du pointeur de fonction ( [conv.fctptr] )) est identique à FT .

[conv.fctptr] Une prvalue de type "pointer vers noexcept function" peut être convertie en une prvalue de type "pointer to function".

Cela implique qu'après la déduction de l'argument du modèle ( [over.over] ¶2 ) pour déterminer la spécialisation de la fonction modèle à ajouter à l'ensemble de surcharge, les types de la fonction modèle la spécialisation et la fonction non-modèle doivent toutes deux être comparées au type de cible, pour décider lequel d'entre eux doit être "sélectionné".

Le bris d'égalité pour préférer la fonction non-modèle ( ¶4 ) n'entre en vigueur que si les types des deux fonctions correspondent au type cible (c'est-à-dire si plus d'une fonction est sélectionnée ).

void (*) (int *) et void (*) (int *) noexcept sont deux types différents. L'expression cast nécessite l'instanciation des spécifications d'exception des deux surcharges, afin de déterminer laquelle d'entre elles correspond au type cible du cast (c'est-à-dire lequel d'entre eux doit être sélectionné) alors que l'expression d'appel ne nécessite que l'instanciation de la spécification d'exception de la surcharge sélectionnée par la résolution de surcharge.

Je pense que Clang a raison d'exiger l'instanciation de la spécification d'exception de la spécialisation du modèle de fonction. Il est plausible que GCC vérifie d'abord si le type de la fonction non-modèle est une correspondance, et si c'est le cas, alors il n'a pas besoin de vérifier le type de spécialisation du modèle de fonction, sachant que même si cela correspond, le bris d'égalité éliminera de toute façon. Il est possible que cela soit également conforme, et la norme est ici ambiguë.


0 commentaires