12 votes

Est-il possible de réinitialiser une référence à une autre valeur en C++ ?

Je sais qu'il est généralement impossible de réinitialiser une référence après qu'elle ait déjà été initialisée.

Cependant, j'ai essayé le code suivant et il s'avère qu'il fonctionne à la fois sur clang++ et g++.

Ma question est la suivante : est-ce que ce qui suit est un C++ valide (défini par le comportement) ?

std::string x = "x";
std::string y = "y";
std::string i = "i";
std::string j = "j";

// now references to x, y
std::pair<std::string &, std::string &> p { x, y };
p.first = "1"; //changes x
p.second = "2"; //changes y
// now references to i, j
new (&p) std::pair<std::string &, std::string &> {i, j};
p.first = "1"; //changes i
p.second = "2"; //changes j

Le code ci-dessus fonctionne sur g++ et clang++, mais est-il bon en C++ ? Merci.

23voto

Passer By Points 9171

Le snippet a un comportement indéfini, mais à peine. Conceptuellement, vous avez détruit les anciennes références et en avez créé de nouvelles, vous n'avez pas lié à nouveau la référence même si vous avez réutilisé la mémoire. Cette partie est tout à fait correcte.

La prise est si la classe réutilisée contient const ou des membres de référence, le nom original de la variable ne peut pas être utilisé pour faire référence au nouvel objet.

new (&p) std::pair<std::string &, std::string &> {i, j};
// p does not refer to the newly constructed object
p.first = "1";  // UB
p.second = "2"; // UB

La solution est simple, dans ce cas

auto p2 = new (&p) std::pair<std::string&, std::string&> {i, j};
p2->first = "1";
p2->second = "2";

Une autre solution est la fonction C++17 std::launder

new (&p) std::pair<std::string &, std::string &> {i, j};
std::launder(&p)->first = "1";
std::launder(&p)->second = "2";

Ces règles vraisemblablement permet au compilateur de faire plus d'optimisations autour des références et des membres const.

20voto

StoryTeller Points 6139

Ma question est la suivante : est-ce que ce qui suit est un C++ valide (défini par le comportement) ?

Cela pourrait être . La clé ici est la paire. Vous avez mis fin à la durée de vie de l'objet paire et démarré la durée de vie d'un autre objet paire dans le même stockage (le placement new fait ces deux choses).

Mais vous devez être conscient que vous ne reliez aucune référence. Vous détruisez un objet qui contenait des références, et vous en créez un nouveau au même endroit. Conceptuellement, vous aviez deux "anciennes" références, et maintenant deux "nouvelles".

Votre code pourrait être correct parce que la paire est une structure simple qui contient une paire de références, et il serait valide si la paire contenait des types trivialement destructibles. Si toutefois le d'tor de tout élément de la paire n'est pas trivial, vous aurez un comportement non défini. Parce que les destructeurs ne seront pas exécutés dans le cadre du placement new.

Le problème comme Passant par noté c'est que vous ne pouvez pas utiliser p pour faire référence au "nouvel objet", car il détient des références. Ce serait la cause de l'UB.

Est-ce un bon C++ ?

C'est discutable. Ce n'est certainement pas quelque chose que l'on s'attendrait à voir souvent.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X