brace-or-equal-Initializer in unions

前端 未结 1 2052
猫巷女王i
猫巷女王i 2021-02-18 16:38

Related: How to initialize a non-POD member in Union

The standard says

At most one non-static data member of a union may have a brace-or-equal-ini

1条回答
  •  感情败类
    2021-02-18 16:48

    C++11 [class.ctor]/5 states:

    A default constructor for a class X is a constructor of class X that can be called without an argument. If there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted (8.4). An implicitly-declared default constructor is an inline public member of its class. A defaulted default constructor for class X is defined as deleted if:

    • X is a union-like class that has a variant member with a non-trivial default constructor,
    • any non-static data member with no brace-or-equal-initializer is of reference type,
    • any non-variant non-static data member of const-qualified type (or array thereof) with no brace-or-equal-initializer does not have a user-provided default constructor,
    • X is a union and all of its variant members are of const-qualified type (or array thereof),
    • X is a non-union class and all members of any anonymous union member are of const-qualified type (or array thereof),
    • any direct or virtual base class, or non-static data member with no brace-or-equal-initializer, has class type M (or array thereof) and either M has no default constructor or overload resolution (13.3) as applied to M’s default constructor results in an ambiguity or in a function that is deleted or inaccessible from the defaulted default constructor, or
    • any direct or virtual base class or non-static data member has a type with a destructor that is deleted or inaccessible from the defaulted default constructor.

    A default constructor is trivial if it is not user-provided and if:

    • its class has no virtual functions (10.3) and no virtual base classes (10.1), and
    • no non-static data member of its class has a brace-or-equal-initializer, and
    • all the direct base classes of its class have trivial default constructors, and
    • for all the non-static data members of its class that are of class type (or array thereof), each such class has a trivial default constructor.

    Otherwise, the default constructor is non-trivial.

    Since the struct Point in the OP has a non-trivial default constructor,

    Point() {}
    

    a defaulted default constructor for a union containing a member of type Point should be defined as deleted according to the first bullet:

    • X is a union-like class that has a variant member with a non-trivial default constructor

    resulting in the program presented in the OP being ill-formed.

    However, the committee seems to consider this to be a defect in the case that a member of a union has a brace-or-equal-initializer, per core working group issue 1623:

    According to 12.1 [class.ctor] paragraph 5,

    A defaulted default constructor for class X is defined as deleted if:

    • X is a union-like class that has a variant member with a non-trivial default constructor,

    • ...

    • X is a union and all of its variant members are of const-qualified type (or array thereof),

    • X is a non-union class and all members of any anonymous union member are of const-qualified type (or array thereof),

    • ...

    Because the presence of a non-static data member initializer is the moral equivalent of a mem-initializer, these rules should probably be modified not to define the generated constructor as deleted when a union member has a non-static data member initializer. (Note the non-normative references in 9.5 [class.union] paragraphs 2-3 and 7.1.6.1 [dcl.type.cv] paragraph 2 that would also need to be updated if this restriction is changed.)

    It would also be helpful to add a requirement to 9.5 [class.union] requiring either a non-static data member initializer or a user-provided constructor if all the members of the union have const-qualified types.

    On a more general note, why is the default constructor defined as deleted just because a member has a non-trivial default constructor? The union itself doesn't know which member is the active one, and default construction won't initialize any members (assuming no brace-or-equal-initializer). It is up to the “owner” of the union to control the lifetime of the active member (if any), and requiring a user-provided constructor is forcing a design pattern that doesn't make sense. Along the same lines, why is the default destructor defined as deleted just because a member has a non-trivial destructor? I would agree with this restriction if it only applied when the union also has a user-provided constructor.

    Issue 1623 has the status "drafting," indicating that the committee believes the issue is probably a defect - why else allow a brace-or-equal-initializer for a union member? - but hasn't yet devoted the time to determine the proper wording for a resolution. Indeed, the paragraph is largely the same in the current C++14 draft N3936 ([class.ctor]/4), except that the wording "any direct or virtual base class or non-static data member" is everywhere replaced by the simpler "any potentially constructed subobject."

    Although the behavior of both compilers is not strictly conforming, I would consider Clang to be behaving in the spirit of the standard. It would appear that GCC becomes confused by the combination of deleted default constructor and brace-or-equal-initializer:

    • it does diagnose the program as ill-formed in the absence of the brace-or-equal-initializer,
    • with the brace-or-equal-initializer present and maximum warnings GCC 4.8.2 performs no initialization of the union at all, and even warns that the members are used uninitialized:

      main.cpp: In function 'int main()':
      main.cpp:17:39: warning: 'u.U::p.Point::y_' is used uninitialized in this function [-Wuninitialized]
           std::cout << u.p.x_ << ":" << u.p.y_ << std::endl;
                                             ^
      main.cpp:17:22: warning: 'u.U::p.Point::x_' is used uninitialized in this function [-Wuninitialized]
           std::cout << u.p.x_ << ":" << u.p.y_ << std::endl;
                            ^
      

    GCC should probably either conform to the standard and diagnose the program as ill-formed, or emulate clang's behavior and generate a proper constructor from the brace-or-equal-initializer.

    0 讨论(0)
提交回复
热议问题