Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Designated initializer different behavior before and after c++20

Tags:

c++

I came across this strange behavior and I can't find a good explanation of it.

The code below will compile successfully before c++20 and only fail if explicit is used.

struct Square {
  int area = 10;

  Square() = default; // only if this is explicit will the compile fail
  Square(const Square& other) = delete;
  Square(Square&& other) = delete;
  Square& operator =(Square&& square) = delete;
  Square& operator =(const Square& square) = delete;
};

int main() {
  Square s = {
      .area = 10
  };
}

Which is strange on its own but turning the compiler to c++20 will make the above code fail with these error messages

gcc

could not convert brace-enclosed initializer list

clang

no matching constructor for initialization of 'Square'


Question:

  • Why does it compile successfully before c++20 unless explicit? In other words what implicit conversion takes place to make that happen?

  • What changed in c++20 that made this code fail to compile?

like image 841
Ricky Spanish Avatar asked May 27 '20 00:05

Ricky Spanish


2 Answers

Why does it compile successfully before c++20 ...

The program is ill-formed prior to C++20.

Designited initialisers did not exist in the language prior to C++20. It compiles because of a language extension.

What changed in c++20 that made it not compile anymore?

The program is still ill-formed in C++20.

Designated initialisers are introduced to the language in C++20, and it appears that the rules are slightly different from what the language extension does. The related rules are (from latest draft):

[dcl.init.list] List-initialization of an object or reference of type T is defined as follows:

  • If the braced-init-list contains a designated-initializer-list, T shall be an aggregate class. ...

  • ...

[dcl.init.aggr] An aggregate is an array or a class ([class]) with

  • no user-declared or inherited constructors ([class.ctor]),

  • ...

The behavioural difference of the language extension prior to C++20 may be related to the change in definition of what is an aggregate, as explained by NathanOliver

like image 121
eerorika Avatar answered Oct 26 '22 09:10

eerorika


In C++20, your class is no longer an aggregate. Since it is not an aggregate, you cannot use a designated initializer. This change is the result of P1008 which removed the presence of user provided defaulted or deleted constructors as qualifying for being an aggregate. The example given for why this change needed to be made was:

struct X {
  int i{4};
  X() = default;
};

int main() {
  X x1(3); // ill-formed - no matching c’tor
  X x2{3}; // compiles!
}

where X x2{3} should not compile but it does because X() = default; doesn't stop it from being an aggregate.

like image 20
NathanOliver Avatar answered Oct 26 '22 09:10

NathanOliver