您可以使用花括号初始化列表作为(默认)模板参数吗?

比尔盖*_*尔盖子 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

template-argument:
    constant-expression
    type-id
    id-expression 
Run Code Online (Sandbox Code Playgroud)

并且由于不是上面列出的三个构造中的任何一个,因此根据当前语法{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 已经从早期版本接受了该程序。