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: Explorateur du compilateur .
Tous les compilateurs conviennent que celui-ci est OK: Explorateur du compilateur .
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: Explorateur du compilateur
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 des 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 eq(__func__, __func__)
n'est pas une expression constante, même si std::equal_to::operator()
est constexpr . Les autres compilateurs ne se plaignent pas: Explorateur du compilateur
Clang ne compilera pas celui-ci non plus. Se plaint que __func__ == __func__
N'est pas une expression constante: Explorateur du compilateur
int main() {
static_assert(__func__ == __func__);
}
__func__
En C++ est un identifiant. En particulier, il fait référence à un objet spécifique. De [dcl.fct.def.general]/8 :
La variable prédéfinie locale fonction
__func__
Est définie comme si une définition du formulairestatic const char __func__[] = "function-name";
avait été fourni, où nom-fonction est une chaîne définie par l'implémentation. Il n'est pas précisé si une telle variable a une adresse distincte de celle de tout autre objet du programme.
En tant que variable prédéfinie locale de fonction , cette définition (comme si) apparaît au début du bloc fonction. Ainsi, toute utilisation de __func__
Dans 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 de __func__
Nomment 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 d'avoir la variable __func__
est également le même objet que le littéral "foo"
renvoie. Autrement dit, la norme n'exige pas que chaque fonction dans laquelle __func__
Apparaisse doit stocker 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, bien que la variable elle-même puisse 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 __func__
De 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 :
Pour un paramètre-modèle non type de référence ou de type pointeur, la valeur de l'expression constante ne doit pas faire référence (ou pour un type pointeur, ne doit pas être l'adresse de):
...
- une variable
__func__
prédéfinie.
Voir, cela semble permettre une certaine fudging par la mise en œuvre. Autrement dit, alors que __func__
Peut techniquement être 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.