Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you use a braced-init-list as a (default) template argument?

I need to define a C++ template that accepts several 3D coordinates as their parameters. When all dimensions of these coordinates are defined as separate integer variables, the parameter list would become exceedingly long - 3 coordinates need 9 parameters, which makes the template hard to use.

Thus, it's highly desirable to declare the templates in a way to use compile-time arrays. Their default arguments should also be declared directly at the location of the template declaration as values, rather than as variable names.

After some experimentation, to my surprise, I found GCC 13 will accept the following C++ program with std=c++20:

#include <cstdio>
#include <array>

template <
    std::array<int, 3> offset = {0, 0, 0}
>
struct Array
{
    void operator() (int i, int j, int k) 
    {
        printf("(%d, %d, %d)\n", i + offset[0], j + offset[1], k + offset[2]);
    }
};

int main(void)
{
    Array arr_default;
    arr_default(0, 0, 0);

    Array<{1, 1, 1}> arr;
    arr(0, 0, 0);

    return 0;
}

However, clang 18 rejects the braced-init-list as invalid:

test2.cpp:5:30: error: expected expression
    5 |         std::array<int, 3> offset = {0, 0, 0}
      |                                     ^
test2.cpp:17:8: error: no viable constructor or deduction guide for deduction of template arguments of 'Array'
   17 |         Array arr_default;
      |               ^
test2.cpp:7:8: note: candidate template ignored: couldn't infer template argument 'offset'
    7 | struct Array
      |        ^
test2.cpp:7:8: note: candidate function template not viable: requires 1 argument, but 0 were provided
    7 | struct Array
      |        ^~~~~
test2.cpp:20:8: error: expected expression
   20 |         Array<{1, 1, 1}> arr;
      |               ^
3 errors generated.

Question

Is it really a legal C++ program? If it is, what syntax should I use to convince clang to accept it? If it's not, how can I fix the code (and should I report a GCC bug for accepting it unquestionably)?

like image 961
比尔盖子 Avatar asked Jan 18 '26 03:01

比尔盖子


1 Answers

Problem

This is CWG 2450 and/or CWG 2049 and although the current grammar does not allow this, it is proposed to be allowed/valid for the reason mentioned below. That means, Gcc is just preemptively allowing the syntax. From CWG 2450:

Since non-type template parameters can now have class types, it would seem to make sense to allow a braced-init-list as a template-argument, but the grammar does not permit it.

(emphasis mine)

In case you're wondering how the current grammar makes does not allow this, from temp.name#1:

template-argument:
    constant-expression
    type-id
    id-expression 

And since {1, 1, 1} is not any of the above three listed constructs, it cannot be used as a template argument as per the current grammar rules.


Is there an alternative and more compatible way to achieve my goals?

Solution

You can explicitly write std::array before the braced init list as shown below:

#include <cstdio>
#include <array>

template <
//------------------------------vvvvvvvvvv----------->added this
    std::array<int, 3> offset = std::array{0, 0, 0}
>
struct Array
{
    void operator() (int i, int j, int k) 
    {
        printf("(%d, %d, %d)\n", i + offset[0], j + offset[1], k + offset[2]);
    }
};

int main(void)
{
    Array arr_default;
    arr_default(0, 0, 0);
//--------vvvvvvvvvv------------------->added this
    Array<std::array{1, 1, 1}> arr;
    arr(0, 0, 0);

    return 0;
}

Note

Also note that clang trunk also starts accepting the program while gcc and msvc already accepted it from earlier versions.

like image 156
Anoop Rana Avatar answered Jan 19 '26 19:01

Anoop Rana



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!