Implicitly defined constructor deleted due to variant member, N3690/N4140 vs N4659/N4727
Asked Answered
A

1

3

My story starts off the same as this person's here:

Unions in C++11: default constructor seems to be deleted

The resolution here (now about three years old) is a bit unsatisfactory, because the "Digging into the standard" that the author did ended up with concluding that the behavior was as described in the standard, but unfortunately the quote is from a Note, and those are supposed to be non-normative (I've been told). Anyways, there's a link to an old bug report on gcc that is claimed to be fixed, and they also claim that the code compiles in clang, however I'm having issues (with the same and similar code). The matter boils down to a whether or not a union-like class with a default initialized variant member should compile whether or not there is another variant member (in the same variant set) that has a non-trivial constructor.

struct X {
  X() {}        //non-trivial default constructor
};

struct U {
  union {
    X x;
    int i{0};   //default member initializer
  };
};

U u;            //error: default constructor deleted

These failed with -std=c++14 and -std=c++17 on gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04) as well as clang version 6.0.0-1ubuntu2.

There seems to be a history of change in the standard, and so I'll put forth what I've found:

N3690/N4140:
[12.1.4/Constructors] ...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

[9.5.2/Unions]... [Note: If any non-static data member of a union has a non-trivial default constructor (12.1), copy constructor (12.8), move constructor (12.8), copy assignment operator (12.8), move assignment operator (12.8), or destructor (12.4), the corresponding member function of the union must be user-provided or it will be implicitly deleted (8.4.3) for the union. — end note]


N4659/N4727:
[15.1.5/Constructors] ...A defaulted default constructor for class X is defined as deleted if:

  • X is a union that has a variant member with a non-trivial default constructor and no variant member of X has a default member initializer

[12.3.3/Unions]... [Note: Absent default member initializers, if any non-static data member of a union has a non-trivial default constructor (12.1), copy constructor (12.8), move constructor (12.8), copy assignment operator (12.8), move assignment operator (12.8), or destructor (12.4), the corresponding member function of the union must be user-provided or it will be implicitly deleted (8.4.3) for the union. — end note]


Anyways, it seems like at one point this code should have failed, but it was thought to be conforming and that gcc had a bug, and it was claimed to be fixed, but now it should succeed, and it doesn't compile (I may not have the chronology perfect here, it would be interesting to know the full story; figuring it out is one step further than I'd like to take) but I guess I'm just wondering what is the correct behavior?

Abney answered 15/11, 2018 at 0:15 Comment(2)
Notes are indeed non-normative. That's a general principle for ISO standards; works the same in ISO C.Ftlb
Related Why does a struct, that has another struct wrapped in a union as a member not compile without an explicit default constructor?Treva
T
3

I believe you are correct, this should be valid code now.

The original wording for the non-normative note that descirbes the old behavior comes from n2544: Unrestricted Unions (Revision 2).

That change you note in the normative section comes from Core defect report 2048: NSDMIs and deleted union default constructors which says:

According to 15.1 [class.ctor] paragraph 4 says,

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,

  • ...

This should make the following example ill-formed:

  struct S {
    S();
  };
  union U {
    S s{};
  } u;

because the default constructor of U is deleted. However, both clang and g++ accept this without error. Should the rule be relaxed for a union with an NSDMI?

and modified [class.default.ctor]p2 as you referenced from N4659/N4727. Additionally we can see that the case mentioned in the DR does indeed work for gcc and clang but not for MSVC see it live on godbolt.

This feels like a bug since [class.default.ctor]p2.1 should cover this case and make it will-formed since the member i has a default member initializer:

X is a union that has a variant member with a non-trivial default constructor and no variant member of X has a default member initializer,

Treva answered 15/11, 2018 at 0:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.