35 votes

Est-ce un comportement défini pour référencer un membre précédent à partir d'une expression de membre ultérieure lors de l'initialisation de l'agrégat?

Considérez les points suivants:

struct mystruct
{
    int i;
    int j;
};

int main(int argc, char* argv[])
{
    mystruct foo{45, foo.i};   

    std::cout << foo.i << ", " << foo.j << std::endl;

    return 0;
}

Notez l'utilisation de l' foo.i dans l'ensemble-l'initialiseur de liste.

g++ 5.2.0 sorties

45, 45

Est-ce bien défini comportement? Est - foo.i de cet agrégat-initialiseur de la garantie de toujours se référer à l'être-créé la structure de l' i élément (c'est - &foo.i ferait référence à une adresse mémoire, par exemple)?

Si j'ajoute explicitement un constructeur mystruct:

mystruct(int i, int j) : i(i), j(j) { }

Puis-je obtenir les mises en garde suivantes:

main.cpp:15:20: warning: 'foo.a::i' is used uninitialized in this function [-Wuninitialized]
     a foo{45, foo.i};
                ^
main.cpp:19:34: warning: 'foo.a::i' is used uninitialized in this function [-Wuninitialized]
     cout << foo.i << ", " << foo.j << endl;

Le code compile et le résultat est:

45, 0

Clairement, ce n'est quelque chose de différent, et je suppose que c'est un comportement indéfini. S'agit-il? Si oui, pourquoi la différence entre cela et quand il n'y a pas de constructeur? Et, comment puis-je obtenir le comportement initial (si c'était bien défini comportement) avec un constructeur?

14voto

Shafik Yaghmour Points 42198

Votre deuxième cas, c'est un comportement indéfini, vous n'êtes plus globale de l'initialisation, il est encore initialisation de la liste, mais dans ce cas, vous avez un utilisateur défini par le constructeur, qui est appelé. Afin de passer le deuxième argument de votre constructeur, il doit évaluer foo.i mais il n'est pas initialisé pourtant, puisque vous n'avez pas encore entré dans le constructeur et, par conséquent, vous produisez une durée indéterminée de la valeur et de la production d'une valeur indéterminée est un comportement indéfini.

Nous avons également la section 12.7 de la Construction et de la destruction [de classe.cdtor] qui dit:

Pour un objet avec un non-trivial constructeur, se référant à un non-membre statique ou de la classe de base de l'objet avant que le constructeur commence l'exécution entraîne un comportement non défini [...]

Donc je ne vois pas une façon d'obtenir votre deuxième exemple de travail comme ton premier exemple, en supposant que le premier exemple est bien valide.

Votre premier cas semble que ce devrait être bien définis, mais je ne peux pas trouver une référence dans le projet de norme qui semble la rendre explicite. C'est peut-être un défaut mais sinon, ce serait un comportement indéfini car la norme ne définit pas le comportement. Ce que la norme ne nous disent, c'est que les initialiseurs sont évaluées dans l'ordre et les effets secondaires sont séquencés, à partir de la section 8.5.4 [dcl.init.liste]:

Au sein de l'initialiseur de liste d'un arc-boutée-init-liste, l'initialiseur-clauses, y compris d'éventuelles résultant de pack les expansions (14.5.3), sont évaluées dans l'ordre dans lequel ils apparaissent. Qui est, à chaque valeur de calcul et de des effets secondaires liés à un initialiseur de la clause est séquencée avant chaque valeur de calcul et de côté effet associé à toute l'initialiseur de la clause qui le suit dans la liste séparée par des virgules de l'initialiseur de la liste. [...]

mais nous n'avons pas de texte explicite en disant les membres sont initialisées après chaque élément est évalué.

On pourrait dire que dans la classe membre initialise semble forcer les membres à être initialisé comme la liste est évalué comme T. C. souligne cet exemple le C++14 projet de norme un peu implique ceci:

Si il y a moins d'initialiseur-clauses dans la liste qu'il y a de les membres de l'ensemble, puis chaque membre n'est pas explicitement initialisée doit être initialisé à partir de son corset ou égal initialiseur ou, s'il y est pas de corset-ou-equalinitializer, du vide d'une liste d'initialiseur (8.5.4). [ Exemple:

struct S { int a; const char* b; int c; int d = b[a]; };
S ss = { 1, "asdf" };

initialise ss.une avec 1, ss.b avec "asdf", ss.c avec la valeur d'un expression de la forme int{} (qui est, 0), et ss.d avec la valeur de ss.b[ss.a] (c'est - 's'),

Cet exemple a été amené par N3653 mais aucune explication n'est fournie pour l'exemple et puisque les exemples sont non-normatifs, nous devons prendre cela avec un grain de sel. Avant C++14 agrégat intialization n'a pas été autorisée lorsque dans la classe des membres intialization était présent.

Bien que si l'on accepte cette interprétation, il n'est pas clair, il s'appliquerait à C++11 car le texte est directement attribuable à un changement qui s'applique à C++14, mais pas à C++11.

MSalters fait valoir que la section 1.9 qui dit:

L'accès à un objet désigné par un volatile glvalue (3.10), la modification d'un objet, l'appel d'une bibliothèque d'e/S la fonction, ou l'appel d'une fonction qui ne prend aucun de ces opérations sont tous les effets secondaires, qui sont des changements dans la l'état de l'environnement d'exécution. [...]

combiné avec:

[...]très valeur de calcul et des effets secondaires liés à un initialiseur de la clause est séquencée avant chaque calcul de la valeur et des effets secondaires associés avec toute l'initialiseur de la clause qui suit [...]

Est suffisante pour garantir à chaque membre de l'agrégat est initialisé comme les éléments de la liste d'initialiseur sont évalués.

Pour référence, si la norme n'impose pas une exigence le comportement n'est pas défini à partir de la section 1.3.24 qui définit un comportement indéfini:

le comportement pour lequel la présente Norme Internationale n'impose pas d'exigences [ Note: un comportement Indéfini peut être prévu lorsque la présente Norme Internationale omet aucune définition explicite de comportement ou [...]

un comportement indéfini n'a pas besoin d'un diagnostic, d' 1.4:

L'ensemble de diagnostiquer les règles se compose de tous syntaxique et sémantique des règles dans la présente Norme Internationale à l'exception de pour que ces règles contenant une notation explicite que "aucun diagnostic n'est requis" ou qui sont décrits comme résultant dans "un comportement indéfini."

12voto

NathanOliver Points 10062

À partir de [dcl.init.aggr] 8.5.1(2)

Lorsqu'un agrégat est initialisée par une liste d'initialiseur, comme spécifié dans 8.5.4, les éléments de la liste d'initialiseur sont pris comme des initialiseurs pour les membres de l'ensemble, l'augmentation de l'indice ou d'un membre de l'ordre. Chaque membre de la copie est initialisé à partir de la correspondante de l'initialiseur de la clause.

c'est moi qui souligne

Et

Au sein de l'initialiseur de liste d'un arc-boutée-init-liste, l'initialiseur-clauses, y compris d'éventuelles résultant de pack expansions (14.5.3), sont évaluées dans l'ordre dans lequel ils apparaissent. Qui est, à chaque calcul de la valeur et des effets secondaires liés à un initialiseur de la clause est séquencée avant chaque calcul de la valeur et des effets secondaires associés avec toute l'initialiseur de la clause qui le suit dans la liste séparée par des virgules de l'initialiseur de la liste.

M'amène à croire que chaque membre de la classe sera initialisé dans l'ordre où elles sont déclarées dans l'initialiseur-liste et depuis foo.i est initialisé avant de nous évaluer pour initialiser j ce doit être défini de comportement.

C'est aussi sauvegardé avec [intro.exécution] 1.9(12)

L'accès à un objet désigné par un volatile glvalue (3.10), la modification d'un objet, l'appel d'une bibliothèque de fonction e/S, ou l'appel d'une fonction qui ne prend aucun de ces opérations sont tous les effets secondaires, qui sont des changements dans l'état de l'environnement d'exécution.

c'est moi qui souligne

Dans ton deuxième exemple, nous ne sommes pas à l'aide d'agrégation de l'initialisation, mais la liste d'initialisation. [dcl.init.liste] 8.5.4(3) a

Liste d'initialisation d'un objet ou d'une référence de type T est défini comme suit:
[...]
- Sinon, si T est un type de classe, les constructeurs sont pris en compte. L'applicables constructeurs sont énumérés et le meilleur est choisi par le biais de la résolution de surcharge (13.3, 13.3.1.7).

Alors maintenant, nous attirons votre constructeur. Lors de l'appel du constructeur foo.i n'a pas été initialisé, donc nous sommes de la copie d'une variable non initialisée qui est un comportement indéfini.

1voto

Serge Ballesta Points 12850

Ma première idée était UB, mais vous êtes complètement dans l'ensemble de l'initialisation de cas. Projet de n4296 pour le C++ 11 spécification est explicite dans le 8.5.1 Agrégats [dcl.init.aggr] paragraphe:

Un agrégat est un tableau ou une classe avec des pas fournie par l'utilisateur constructeurs , sans privés ou protégés non-membres de données statiques, pas de classes de base, et pas de fonctions virtuelles

Plus tard:

Lorsqu'un agrégat est initialisée par une liste d'initialiseur, comme spécifié dans 8.5.4, les éléments de la liste d'initialiseur sont pris comme des initialiseurs pour les membres de l'ensemble, dans l'augmentation de l'indice ou d'un membre de l'ordre

(accent sur la mienne)

Ma compréhension est qu' mystruct foo{45, foo.i}; initialise tout d'abord, foo.i avec 45, puis en foo.j avec foo.i.

Je n'oserai pas l'utiliser dans le code réel, de toute façon, parce que même si je crois qu'elle est définie par le standard, j'aurais peur qu'un compilateur programmeur a pensé différemment...

-2voto

cm161 Points 52

Comment puis-je obtenir le comportement initial (s'il s'agissait d'un comportement bien défini) avec un constructeur défini par l'utilisateur?

Passage paramètre par référence pour ce paramètre qui fait référence au paramètre précédemment construit de l'objet en cours de construction, comme suit:

  mystruct(int i, int& j):i(i),j(j)
 

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