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.
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)?
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?
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;
}
Also note that clang trunk also starts accepting the program while gcc and msvc already accepted it from earlier versions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With