109 votes

Passe un objet C++ dans son propre constructeur juridique ?

Je suis surpris qu'accidentellement découvrir que les œuvres suivantes:

#include <iostream>            
int main(int argc, char** argv)
{
  struct Foo {
    Foo(Foo& bar) {
      std::cout << &bar << std::endl;
    }
  };
  Foo foo(foo); // I can't believe this works...
  std::cout << &foo << std::endl; // but it does...
}

Je suis de passage à l'adresse de l'objet construit dans son propre constructeur. Cela ressemble à une définition circulaire au niveau de la source. Les normes vraiment vous permettre de passer un objet dans une fonction avant que l'objet est même construit ou est-ce un comportement indéfini?

Je suppose que c'est pas ça bizarre étant donné que toutes les fonctions de membre de classe ont déjà un pointeur vers les données de leur instance de classe comme un paramètre implicite. Et la présentation des données des membres est fixé au moment de la compilation.

Remarque, je ne demande PAS si c'est utile ou une bonne idée; je suis juste bricoler pour en savoir plus sur les classes.

65voto

Shafik Yaghmour Points 42198

Ce n'est pas un comportement indéfini puisque vous n'êtes pas en produisant une valeur indéterminée. Si nous essayons de cela avec clang nous voir l'avertissement ci-après (voir en direct):

avertissement: la variable 'foo' est non initialisée quand il est utilisé dans sa propre initialisation [-Wuninitialized]

Il est valable un avertissement depuis la production et une valeur indéterminée à partir d'un non initialisée variable automatique est un comportement indéfini , mais dans ce cas, vous êtes juste de liaison de référence et de prendre l'adresse de la variable dans le constructeur qui ne va pas produire une valeur indéterminée et est valide. Je cite les articles pertinents du projet de norme ci-dessous. J'ai également référence à deux rapports de défaut 363 et 453, ce qui indique aussi ce qui est bien définie, bien que l'on est toujours ouverte, la langue semble à l'appui de cette légale.

La section pertinente du projet de C++14 norme serait d' 3.3.2 Point de la déclaration [de base.la portée.pdecl]:

Le point de la déclaration d'un nom est immédiatement après sa complète demande de déclaration (article 8) et avant son initialiseur (le cas échéant), sauf que indiqué ci-dessous. [ Exemple:

unsigned char x = 12;
{ unsigned char x = x; }

Voici la deuxième x est initialisé avec son propre (durée indéterminée). fin de l'exemple ]

3.8 Durée de vie des objets [de base.la vie]:

De même, avant la durée de vie d'un objet a commencé, mais après la de stockage dont l'objet s'occuper a été alloué ou, après la durée de vie d'un objet est terminé, et avant le stockage de l'objet occupés est réutilisé ou libérés, tout glvalue qui fait référence à l'objet d'origine peuvent être utilisés, mais uniquement dans certaines façons. Pour un objet en cours de construction ou de destruction, voir 12.7. Dans le cas contraire, un glvalue fait référence attribué de stockage (3.7.4.2), et en utilisant les propriétés de la glvalue qui ne dépendent pas de sa valeur est bien définie. L' le programme a un comportement indéfini si:

  • une lvalue-à-rvalue de conversion (4.1) est appliquée à une telle glvalue,

  • le glvalue est utilisé pour accéder à un non-membre de données statiques ou appeler un non-statique de la fonction de membre de la objet, ou

  • le glvalue est lié à une référence à une classe de base virtuelle (8.5.3), ou

  • le glvalue est utilisé comme opérande d'un dynamic_cast (5.2.7) ou que l'opérande de typeid.

et je pense que peut - 8.5 Initialiseurs [dcl.init]:

[...]Lorsque le stockage d'un objet avec ou automatique dynamique de la durée de stockage est obtenu, l'objet a une valeur indéterminée, et si pas d'initialisation est effectuée pour l'objet, celui-ci conserve une valeur indéterminée jusqu'à ce que la valeur est remplacé (5.17). [ Remarque: les Objets statiques ou thread durée de stockage sont initialisé à zéro, voir 3.6.2. - fin de la remarque ] Si une période indéterminée, la valeur est produite par une évaluation, le comportement est indéfini, sauf dans le cas suivants

Des Rapports De Défaut

Pertinentes de rapport de défaut semble être active problème 453: les Références ne peuvent se lier à la "validité" des objets et Fermée numéro 363: l'Initialisation de la classe de l'auto . Ce dernier indique qu'il est valide et dit:

3.8 [de base.la vie] le paragraphe 6 indique que les références ici sont valides. Il est autorisé de prendre l'adresse d'un objet de classe avant qu'il soit complètement initialisé, et il est permis de le passer en argument à un paramètre de référence tant que la référence peut se lier directement. [...]

l'ancien est encore ouvert, mais la langue qui est proposé dans le sur le indique également ce doit être valide.

15voto

MSalters Points 74024

Le constructeur est appelé à un point où la mémoire est allouée pour l'objet-à-être. À ce stade, aucun objet n'existe à cet endroit (ou peut-être un objet avec un trivial destructeur). En outre, l' this pointeur désigne que la mémoire et la mémoire est correctement aligné.

Depuis, il est alloué et alignés sur la mémoire, nous pouvons nous référer à l'aide de lvalue expressions de Foo ( Foo&). Ce que l'on peut pas encore faire est d'avoir une lvalue-à-rvalue de conversion. C'est uniquement autorisée après le corps du constructeur est entré.

Dans ce cas, le code essaie juste d'imprimer &bar à l'intérieur du corps du constructeur. Il serait même légal pour imprimer bar.member ici. Depuis le corps du constructeur est entré, Foo objet existe et que ses membres peuvent être lus.

Ce qui nous laisse avec un petit détail, et c'est la recherche d'un nom. En Foo foo(foo), la première foo introduit le nom dans le champ d'application et le second foo renvoie donc dos à la juste déclaré nom. C'est pourquoi, int x = x n'est pas valide, mais int x = sizeof(x) est valide.

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