比尔盖*_*尔盖子 15 c++ language-lawyer list-initialization braced-init-list c++-templates
我需要定义一个接受多个 3D 坐标作为参数的 C++ 模板。当这些坐标的所有维度都定义为单独的整数变量时,参数列表将变得非常长 - 3 个坐标需要 9 个参数,这使得模板难以使用。
因此,非常希望以使用编译时数组的方式声明模板。它们的默认参数也应该直接在模板声明的位置声明为值,而不是变量名。
经过一些实验,令我惊讶的是,我发现 GCC 13 将接受以下 C++ 程序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;
}
Run Code Online (Sandbox Code Playgroud)
然而,clang 18 拒绝使用braced-init-list,因为它无效:
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.
Run Code Online (Sandbox Code Playgroud)
它真的是合法的C++程序吗?如果是,我应该使用什么语法来说服 clang 接受它?如果不是,我该如何修复代码(并且我应该报告 GCC 错误以毫无疑问地接受它)?
use*_*570 17
这是CWG 2450和/或CWG 2049,尽管当前语法不允许这样做,但由于下述原因建议允许/有效。这意味着,Gcc 只是抢先允许语法。来自 CWG 2450:
由于非类型模板参数现在可以具有类类型,因此允许花括号初始化列表作为模板参数似乎是有意义的,但语法不允许这样做。
(强调我的)
如果您想知道当前语法如何不允许这样做,来自temp.name#1:
Run Code Online (Sandbox Code Playgroud)template-argument: constant-expression type-id id-expression
并且由于不是上面列出的三个构造中的任何一个,因此根据当前语法{1, 1, 1}
规则,它不能用作模板参数。
是否有替代且更兼容的方式来实现我的目标?
您可以在花括号初始化列表之前显式编写,std::array
如下所示:
#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;
}
Run Code Online (Sandbox Code Playgroud)
另请注意,clang trunk也开始接受该程序,而 gcc 和 msvc 已经从早期版本接受了该程序。