Simplifions avec cet exemple :
struct a {
template <typename... Args>
void va(Args...) {}
template <typename X>
void x(X) {}
void y(int) {}
};
using no_args = void(a::*)();
using int_arg = void(a::*)(int);
Et essayons les quatre choses suivantes :
reinterpret_cast<no_args>(&MEMBER_FUNCTION); // (1)
(no_args) &MEMBER_FUNCTION; // (2)
(no_args) static_cast<int_arg>(&MEMBER_FUNCTION); // (3)
int_arg temp = &MEMBER_FUNCTION; (no_args) temp; // (4)
(Remplaçant MEMBER_FUNCTION
con &a::va<int>
, &a::x<int>
y &a::y
).
clang les compile tous.
gcc compile tout sauf (2) avec &a::va<int>
y &a::x<int>
.
MSVC compile tout sauf (1) et (2) avec &a::va<int>
(mais il n'y a pas de problème avec &a::x<int>
).
Remarquez que (3) est essentiellement le même que (4).
https://gcc.godbolt.org/z/a2qqyo montrant un exemple de cela.
Ce que vous pouvez voir de cela est que &MEMBER_FUNCTION
n'est pas résolu en un pointeur de fonction membre spécifique dans le cas des modèles, mais s'il est résolu, il est autorisé à le réinterpréter en un autre type de pointeur de fonction membre.
Ce que dit la norme :
[over.over]/1 :
L'utilisation d'un nom de fonction surchargé sans arguments est résolue dans certains contextes en une fonction, un pointeur de fonction ou un pointeur de fonction membre pour une fonction spécifique de l'ensemble de surcharge. Un nom de modèle de fonction est considéré comme le nom d'un ensemble de fonctions surchargées dans de tels contextes. Une fonction de type F est sélectionnée pour le type de fonction FT du type cible requis dans le contexte si F (après avoir éventuellement appliqué la conversion de pointeur de fonction) est identique à FT. [Note : C'est-à-dire que la classe dont la fonction est membre est ignorée lors de la correspondance d'un type de fonction pointeur à membre. -fin de la note ] La cible peut être :
[...]
- une conversion de type explicite ([expr.type.conv], [expr.static.cast], [expr.cast])
Un exemple est donné plus loin :
int f(double);
int f(int);
void g() {
(int (*)(int))&f; // cast expression as selector
}
Et quelques autres citations sur les modèles :
[temp.deduct.funcaddr]/1 :
Les arguments du modèle peuvent être déduits du type spécifié lors de la prise de l'adresse d'une fonction surchargée. Le type de fonction du modèle de fonction et le type spécifié sont utilisés comme types de P et A, et la déduction est effectuée comme décrit dans [temp.deduct.type].
[temp.arg.explicit]/4
[...] si une liste d'arguments de modèle est spécifiée et qu'elle identifie, avec les arguments de modèle par défaut, une seule spécialisation de modèle de fonction, alors le template-id est une lvalue pour la spécialisation de modèle de fonction.
Il semble que MSVC ait raison.
&a::va<int>
n'est pas résolu à moins que vous ne l'assigniez/transfériez à un fichier de type void(a::*)(int)
. Vous devriez également être en mesure de l'attribuer à void(a::*)(int, char)
o void(a::*)(int, double, char)
où le Args
se déduirait comme suit { int, char }
y { int, double, char }
respectivement. Cela signifie que (no_args) &a::va<int>
devrait échouer, car il existe de nombreux ensembles possibles de Args
il pourrait s'agir de (tous commencent par int
et clang le résout par excès de zèle), et aucun d'entre eux ne prend de paramètres nuls, donc (no_args) &a::va<int>
est un static_cast
qui devrait échouer.
Quant à &a::x<int>
il n'y a qu'une seule fonction possible, donc elle devrait fonctionner exactement de la même manière que la fonction &a::y
(Mais gcc ne l'a toujours pas résolu).
2 votes
Incluez les erreurs du compilateur dans votre question.
0 votes
Impossible de convertir les fonctions. Voulez-vous dire le journal complet de gcc.godbolt ?
0 votes
Il n'est pas nécessaire que le journal soit complet, mais il faut inclure le message d'erreur (et les lignes d'information suivantes) pour les erreurs de compilation.
1 votes
F
est un pointeur sur une fonction membre qui ne prend aucun paramètre.va<int>
prend un paramètre. Il en va de même pourX<int>
. Depuis quand un pointeur sur une fonction qui prend un paramètre peut-il être converti en un pointeur sur une autre fonction qui ne prend aucun paramètre, qu'il s'agisse de fonctions normales ou de méthodes de classe ? Cette conversion ne devrait pas fonctionner.y
prend également unint
paramètre. Cette conversion ne devrait pas fonctionner non plus. Mais qu'en est-il de l'utilisation d'un cast explicite...0 votes
Pourquoi ne serait-ce pas une UB ? J'aimerais également savoir s'il est légal de convertir une fonction avec argument en une fonction sans argument. Je chercherai également la réponse)
0 votes
Norme C11, 6.3.2.3 §8 : : Un pointeur sur une fonction d'un type peut être converti en un pointeur sur une fonction d'un autre type et inversement. stackoverflow.com/a/5579907/558098