2

Suppose we have this code:

class A {
public:
    A() = default;    
    A(const A&) = delete;
    ~A() = default;
};

class B {
public:    
    B() : a{} { }    
    A a[1];
};

int main() 
{
    B b;
}

This code compiles on latest GCC 9.2, Clang 9.2, and MSVC 19.22.

But when I change A default destructor to ~A() { } GCC returns error use of deleted function 'A::A(const A&)'. Clang and MSVC still compile.

When I write the copy constructor of A, GCC compiles, but at runtime this constructor was never called. What does GCC need the copy constructor for? Is it GCC bug? (I've tried about all GCC versions on GodBolt.org, same error.)

L. F.
  • 19,445
  • 8
  • 48
  • 82
  • 1
    I tried, but I failed to answer... From what I found, the huge difference between the two versions is that if you `= default;`, the class becomes trivial, and even trivially copyable (both `clang` and `gcc` reports `true` for `std::is_trivially_copyable`), and in C++11, I think that the standard is on the same page. In particular, a deleted copy-constructor can be considered as trivial... I think in C++17, the rules are different thanks to guaranteed copy-elision. I am not sure if clang is correct or GCC... The move constructor is deleted in both cases, so that's not the problem here. – Holt Nov 06 '19 at 09:30
  • 100% duplicate of [List-initialization of an array without temporaries - not working in GCC](https://stackoverflow.com/questions/55904342/list-initialization-of-an-array-without-temporaries-not-working-in-gcc) – Language Lawyer Nov 06 '19 at 16:14
  • @LanguageLawyer This one doesn't involve [\[dcl.init.list\]/3.4](https://timsong-cpp.github.io/cppwp/n4659/dcl.init.list#3.4), so 50% duplicate. – L. F. Nov 07 '19 at 00:58

1 Answers1

1

This is a GCC bug.

The default constructor of B uses aggregate initialization to initialize a with no initializers. [dcl.init.aggr]/8:

If there are fewer initializer-clauses in the list than there are elements in a non-union aggregate, then each element not explicitly initialized is initialized as follows:

  • If the element has a default member initializer ([class.mem]), the element is initialized from that initializer.

  • Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list ([dcl.init.list]).

  • Otherwise, the program is ill-formed.

[...]

So a[0] is copy-initialized from {}, which is copy-list-initialization. This is probably where GCC starts to get confused — copy-initialization does not necessarily involve the copy constructor.

[dcl.init.list]/3.4:

List-initialization of an object or reference of type T is defined as follows:

  • [...]

  • Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.

  • [...]

Therefore, the default constructor of A is used directly. There's no copy constructor involved. Nor triviality.


In case you are worrying about the difference between C++11 and C++17, N3337 [dcl.init.aggr]/7:

If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from an empty initializer list ([dcl.init.list]). [...]

Copy-initialization isn't even involved here. And N3337 [dcl.init.list]/3.1:

List-initialization of an object or reference of type T is defined as follows:

  • If the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.

  • [...]

No change.

L. F.
  • 19,445
  • 8
  • 48
  • 82
  • Do you know if GCC is awared about this? – ivan bubnikov Nov 06 '19 at 10:25
  • @ivanbubnikov I can't find a relevant bug report by [searching the bugzilla for "aggregate copy"](https://gcc.gnu.org/bugzilla/buglist.cgi?quicksearch=aggregate%20copy%20). – L. F. Nov 06 '19 at 12:04