Est-ce un C ++ valide?
int main() {
constexpr auto sz = __func__ - __func__;
return sz;
}
GCC et MSVC pensent que c'est OK, Clang pense que ce n'est pas le cas: Compiler Explorer .
Tous les compilateurs conviennent que celui-ci est OK: Explorateur de compilateurs .
int main() {
constexpr auto p = __func__;
constexpr auto p2 = p;
constexpr auto sz = p2 - p;
return sz;
}
Clang n'aime pas encore celui-ci, mais les autres sont d'accord avec lui: Compiler Explorer
int main() {
constexpr auto p = __func__;
constexpr auto p2 = __func__;
constexpr auto sz = p2 - p;
return sz;
}
Qu'est-ce qu'il y a ici? Je pense que l'arithmétique sur les pointeurs non liés est un comportement indéfini mais __func__
renvoie le même pointeur, non? Je ne suis pas sûr, alors j'ai pensé que je pourrais le tester. Si je me souviens bien, std::equal_to
peut comparer des pointeurs indépendants sans comportement indéfini:
#include <functional>
int main() {
constexpr std::equal_to<const char*> eq{};
static_assert(eq(__func__, __func__));
}
Clang pense que ce eq(__func__, __func__)
n'est pas une expression constante, même si std::equal_to::operator()
c'est constexpr . Les autres compilateurs ne se plaignent pas: Explorateur de compilateurs
Clang ne compilera pas celui-ci non plus. Se plaint que ce __func__ == __func__
n'est pas une expression constante: Explorateur du compilateur
int main() {
static_assert(__func__ == __func__);
}
__func__
est comme sistatic const char __func__[] = "function-name";
et cet équivalent est accepté Démo ...__func__
et l'utilisez dans le static_assert ...__func__
entièrement supprimé de l'évaluation constexpr.Réponses:
__func__
en C ++ est un identifiant. En particulier, il fait référence à un objet spécifique. Depuis [dcl.fct.def.general] / 8 :En tant que variable prédéfinie locale de fonction , cette définition (comme si) apparaît au début du bloc fonction. En tant que tel, toute utilisation de l'
__func__
intérieur de ce bloc fera référence à cette variable.Quant à la partie "tout autre objet", une variable définit un objet.
__func__
nomme l'objet défini par cette variable. Par conséquent, dans une fonction, toutes les utilisations du__func__
nom de la même variable. Ce qui n'est pas défini, c'est si cette variable est un objet distinct des autres objets.Autrement dit, si vous êtes dans une fonction nommée
foo
et que vous avez utilisé le littéral"foo"
ailleurs dans le problème, il n'est pas interdit à une implémentation que la variable__func__
soit également le même objet que le littéral"foo"
renvoie. Autrement dit, la norme n'exige pas que chaque fonction dans laquelle__func__
apparaît s'affiche stocke des données distinctes du littéral de chaîne lui-même.Maintenant, la règle "comme si" de C ++ permet aux implémentations de s'en écarter, mais elles ne peuvent pas le faire d'une manière qui serait détectable. Ainsi, alors que la variable elle-même peut ou non avoir une adresse distincte des autres objets, les utilisations de
__func__
dans la même fonction doivent se comporter comme si elles faisaient référence au même objet.Clang ne semble pas implémenter de
__func__
cette façon. Il semble l'implémenter comme s'il renvoyait un littéral de chaîne de valeur du nom de la fonction. Deux littéraux de chaîne distincts n'ont pas à faire référence au même objet, donc la soustraction de pointeurs vers eux est UB. Et un comportement indéfini dans un contexte d'expression constante est mal formé.La seule chose qui me fait hésiter à dire que Clang a 100% tort ici est [temp.arg.nontype] / 2 :
Voir, cela semble permettre une certaine fudging par la mise en œuvre. Autrement dit, bien qu'il
__func__
puisse être techniquement une expression constante, vous ne pouvez pas l'utiliser dans un paramètre de modèle. Il est traité comme un littéral de chaîne, même s'il s'agit techniquement d'une variable.Donc, à un certain niveau, je dirais que la norme parle des deux côtés de sa bouche.
la source
__func__
peut être une expression constante dans tous les cas de ma question, non? Le code aurait donc dû être compilé.__func__
l'adresse est la même que celle d'un autre objet, et dans la deuxième occurrence de__func__
celui-ci ne l'est pas? Certes, cela ne signifie pas que l'adresse diffère entre les deux instances, mais je suis toujours confus!__func__
n'est pas une macro; c'est un identifiant qui nomme une variable spécifique et donc un objet spécifique. Par conséquent, toute utilisation de__func__
dans la même fonction doit entraîner une valeur gl qui fait référence au même objet. Ou plus précisément, il ne peut pas être mis en œuvre de telle manière que ce ne serait pas le cas.new
dessus, il ne va nulle part jusqu'à la fin du programme.