Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template argument deduction for aggregate template with array

Consider the following program:

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

int main()
{
   A a{1}; // only one value provided for 2-element array
   return a.t[0];
}

In C++20 mode it compiles fine in gcc, but fails in Visual Studio with the error:

<source>(6): error C2641: cannot deduce template arguments for 'A'
<source>(6): error C2780: 'A<T> A(void)': expects 0 arguments - 1 provided
<source>(2): note: see declaration of 'A'
<source>(6): error C2784: 'A<T> A(A<T>)': could not deduce template argument for 'A<T>' from 'int'
<source>(2): note: see declaration of 'A'
<source>(6): error C2784: 'A<T> A(A<T>)': could not deduce template argument for 'A<T>' from 'int'
<source>(2): note: see declaration of 'A'
<source>(6): error C2780: 'A<T> A(T,T)': expects 2 arguments - 1 provided
<source>(2): note: see declaration of 'A'

https://gcc.godbolt.org/z/6ejfW8G77

Which of the two compilers is right here?

(Clang is out of question here since it does not support parenthesized initialization of aggregates yet).

Update: Microsoft admitted the bug and promised the fix soon: https://developercommunity.visualstudio.com/t/template-argument-deduction-fails-in-case-of-aggre/1467260

like image 277
Fedor Avatar asked Jul 03 '21 20:07

Fedor


1 Answers

GCC is right, the code should be accepted, and the type of a should be deduced as A<int> using class template argument deduction (CTAD) for aggregates.

N4868 (closest to the published C++20 standard) [over.match.class.deduct]/1 says (I've excluded some parts that aren't relevant here):

When resolving a placeholder for a deduced class type where the template-name names a primary class template C, a set of functions and function templates, called the guides of C, is formed comprising:
[...]
In addition, if C is defined and its definition satisfies the conditions for an aggregate class 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

  • 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,
    [...]

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), where

  • [...]
  • otherwise, Ti is the declared type of ei,

[...]

Instantiations of A are aggregates. There's only x1, the 1. The bullet about not considering brace elision doesn't apply, because we have a subaggregate (T t[2]) that is an array that has a dependent type but not a value-dependent bound. So brace elision is considered, and the 1 would initialize t[0], which is e1, and is of type T.

So, the aggregate deduction candidate is

template<typename T> A<T> F(T)

which can be used to deduce A<int>.

MSVC fails to generate this template. The error message shows that it incorrectly generates one with two function parameters.

CTAD for aggregates was specified in P1816, but the rule that allows this to work appeared in an additional paper, P2082, that fixed several problems. MSVC claims to implement both since VS 2019 16.7, but still rejects the code as of 16.10.3.

As an additional data point, EDG 6.2 in strict C++20 mode accepts the code.

like image 188
bogdan Avatar answered Oct 01 '22 11:10

bogdan