web-dev-qa-db-fra.com

La différence de deux instances constexpr de pointeurs __func__ est-elle toujours constexpr?

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__);
}
14
Ayxan

__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 formulaire

static 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.

13
Nicol Bolas