Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructor interferes with member variable designated initializer?

For a while now, one has been able to use "designated initializer" in GCC:

struct CC{
    double a_;
    double b_;
};

CC cc{.a_ = 1., .b_ = 2.}; assert(cc.a_ == 1. and cc.b_ == 2.); // ok
CC cc{.bla = 0., .bli = 0.}; // compile error

However when I add a constructor the labels are ignored.

struct CC{
    double a_;
    double b_;
    CC(double a, double b) : a_{a}, b_{b}{}
};

CC cc{.a_ = 1., .b_ = 2.}; assert(cc.a_ == 1. and cc.b_ == 2.); // ok
CC cc{.b_ = 2., .a_ = 1.}; // compiles but labels don't matter only the order, confusing
CC cc{.bla = 2., .bli = 1.}; // compiles but labels don't matter, confusing

In other words the initializer syntax with a constructor make the label behave just as a comment!, which can be very confusing, but above all, it is very odd.

I discovered this accidentally, with gcc 8.1 -std=c++2a.

Is this the expected behavior?

Reference: https://en.cppreference.com/w/cpp/language/aggregate_initialization

EDIT 2022: Now that compilers support -std=c++20 the behavior is correct. All the GCC version that accept -std=c++20 (10.1 and above) also accept the above code or give an error when it should.

https://godbolt.org/z/h3eM3T7jK

like image 440
alfC Avatar asked Sep 11 '18 18:09

alfC


Video Answer


2 Answers

Labels for designated initializers should not be ignored by compilers. All three of those examples with a constructor should be ill-formed.

You are likely getting this behavior due to a conflict between the C++20 designated initializer feature and GCC's C-style designated initializers which you are implicitly accessing due to GCC just giving them to you. If GCC were properly C++20-compiliant, once you gave the type a constructor, it would cease to be an aggregate and thus designated initializer usage would be ill-formed.

Basically, this is a driver bug caused by non-C++-standard behavior the compiler gives you by default. Odds are good that if you turn off this feature, you would get a proper compiler error for those cases.

like image 91
Nicol Bolas Avatar answered Sep 28 '22 01:09

Nicol Bolas


This is a gcc bug, this still builds even with -pedantic in which we should receive warnings for any extensions

...to obtain all the diagnostics required by the standard, you should also specify -pedantic ...

and gcc claims to support the P0329R4: Designated initializers proposal for C++2a mode according to the C++ Standards Support in GCC page:

Language Feature | Proposal | Available in GCC?
...
Designated initializers | P0329R4 | 8

In order to use Designated initializers the type should be aggregate [dcl.init.list]p3.1:

If the braced-init-list contains a designated-initializer-list, T shall be an aggregate class. The ordered identifiers in the designators of the designated-initializer-list shall form a subsequence of the ordered identifiers in the direct non-static data members of T. Aggregate initialization is performed (11.6.1). [ Example:

struct A { int x; int y; int z; };
A a{.y = 2, .x = 1}; // error: designator order does not match declaration order
A b{.x = 1, .z = 2}; // OK, b.y initialized to 0

—end example ]

CC is not an aggregate according to [dcl.init.aggr]:

An aggregate is an array or a class (Clause 12) with
- (1.1) — no user-provided, explicit, or inherited constructors (15.1),
....

gcc bug report

If we look at gcc bug report: Incorrect overload resolution when using designated initializers we see in this given example:

Another test case, reduced from Chromium 70.0.3538.9 and accepted by clang:

  struct S { void *a; int b; };
  void f(S);
  void g() { f({.b = 1}); }

This fails with

  bug.cc: In function ‘void g()’:
  bug.cc:3:24: error: could not convert ‘{1}’ from ‘<brace-enclosed initializer list>’ to ‘S’
   void g() { f({.b = 1}); }
                        ^

The error suggests the field names are simply ignored entirely during overload resolution, which also explains the behaviour of the originally reported code.

It seems gcc ignores field names during overload resolution. Which would explain the strange behavior you are seeing and that we obtain correct diagnostics when we remove the constructor.

like image 33
Shafik Yaghmour Avatar answered Sep 28 '22 02:09

Shafik Yaghmour