Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the braced-init-list not supported in an aggregate deduction but brace elision is supported?

Why is the braced-init-list not supported in an aggregate deduction but brace elision is supported?

#include <iostream>
template<typename T>
struct Test{
    T t[2];
};
int main(){
  Test t{{1,2}};  // #1
  // Test t1{1,2} // #2
}

#1 is rejected by GCC while #2 would be accepted by GCC.

According to over.match.class.deduct#1

In addition, if C is defined and its definition satisfies the conditions for an aggregate class ([dcl.init.aggr]) with the assumption that any dependent base class has no virtual functions and no virtual base classes, and the initializer is a non-empty braced-init-list or parenthesized expression-list, and there are no deduction-guides for C, the set contains an additional function template, called the aggregate deduction candidate, defined as follows. Let X1,...,XN be the elements of the initializer-list or designated-initializer-list of the braced-init-list, or of the expression-list. For each Xi, let ei be the corresponding aggregate element of C or of one of its (possibly recursive) subaggregates that would be initialized by Xi if

  • [1.5] brace elision is not considered for any aggregate element that has a dependent non-array type or an array type with a value-dependent bound, and
  • [1.6] each non-trailing aggregate element that is a pack expansion is assumed to correspond to no elements of the initializer list, and
  • [1.7] a trailing aggregate element that is a pack expansion is assumed to correspond to all remaining elements of the initializer list (if any).

If there is no such aggregate element ei for any Xi, the aggregate deduction candidate is not added to the set. The aggregate deduction candidate is derived as above from a hypothetical constructor C(T1,...,Tn)

  • if ei is of array type and xi is a braced-init-list or string-literal, Ti is an rvalue reference to the declared type of ei

In my example, the x1 is a braced-init-list({1,2}) and the type of e1 is of array type T[2], hence the Constructor should be the form C(T(&&)[2]) and the template argument can be deduced for T(&&)[2] from {1,2} as per temp.deduct.call#1

Why is the above example rejected by GCC? GCC whereas accepts the brace elision way? How to interpret this example? Is that considered the bug of GCC or something that I misunderstand?


Another issue I think it's weird is that if Xi is a braced-init-list that should have been used to initialize the subaggregate, if bullet [1.5] is true, then Xi will be used to initialize the element of the subaggregate. What does it mean?

UPDATE

After digging in further in p2082r1. From its context, it seems that the wording aggregate element means an element of aggregate type instead of the aggregate's element. IIUC, if the bullet [1.5], [1.6], [1.7] is satisfied, ei will be the aggregate element. However, if these bullets are all not conformed, what will the ei be? It seems under-specified here.

like image 380
xmh0511 Avatar asked Apr 27 '21 09:04

xmh0511


2 Answers

Digging in further in P2082r1, I think GCC is right on this example. Since the subaggregate T t[2] of Test has an array of the non-dependent value bound, hence the corresponding e0 and e1 is t0,t1, respectively. Hence the derived guide is template<typename T> Test(T, T).

When this guide is used as a constructor for the hypothetical class type to participate in overload resolution, [over.match.list] is applied here since the initializer is {{1,2}}, since there is no initializer-list constructor, the elements of the initializer-list are used as the arguments here, as aforementioned, the candidate template<typename T> Test(T, T) has two parameters but only one argument({1,2}) supplies, hence the overload resolution is a failure, hence the program is ill-formed.

However, I still think the rule is not clear here and should be improved to make the meaning more explicit.

like image 120
xmh0511 Avatar answered Oct 19 '22 06:10

xmh0511


This is a bug in GCC up to version 11.2, and fixed in GCC 11.3. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101344 for detailed explanation what caused this bug.

Even earlier versions of GCC can compile similar and even more difficult case A (as was mentioned in the comments to the question):

template<typename T, int N>
struct A { T t[N]; };

template<typename T>
struct B { T t[2]; };

int main()
{
   [[maybe_unused]] A a{{1, 2}}; //ok in GCC
   [[maybe_unused]] B b{{1, 2}}; //ok in GCC >= 11.3
}

Demo: https://gcc.godbolt.org/z/4rcYj6fah

like image 1
Fedor Avatar answered Oct 19 '22 04:10

Fedor