是否可以将花括号初始化列表作为模板参数传递?

iak*_*aks 2 c++ template-meta-programming c++20 ctad non-type-template-parameter

我有这门课

template <typename ValueType, std::size_t Size>
struct ArrayPrimitive
{
  constexpr ArrayPrimitive(const ValueType (&array)[Size]) {
    std::copy(array, array + Size, data_);
  }
  ValueType data_[Size];
};
Run Code Online (Sandbox Code Playgroud)

这是我尝试将字符串文字传递为 NTTP 的包装器。然后我利用 CTAD 声明这个变量模板

template <ArrayPrimitive array>
std::integral_constant<decltype(array), array> arr;
Run Code Online (Sandbox Code Playgroud)

然后在代码中使用它

for (auto i : arr<{{2,4,6}}>.value.data_) std::cout << i << std::endl;

它编译并正确打印所有值。

我使用的编译器是 gcc13.2,我知道它目前无法在 clang 上运行,因为 c++20 对 NTTP 类的支持还不存在。另外,VS Code 的代码分析器确实不喜欢模板实例化中的括号,这有点奇怪,因为它没有arr<"123">.

我想知道它是否符合标准并且不会因未来的变化而中断。

编辑:查看错误报告GCC bug 111277的答案,看来 GCC 确实实现了 CWG 2450,将其置于预期行为领域,但仅适用于这一个编译器。

EDIT2:如果我这样写的话,它似乎也可以用 MSVC 编译https://gcc.godbolt.org/z/1sThrse3x

Jan*_*tke 5

该代码arr<{{2,4,6}}>在语法上无效。如果您查看template-argument的语法,那么您会注意到它不支持扩展到花括号初始化列表arr<ArrayPrimitive{2, 4, 6}>现在你必须写。

GCC 允许的事实arr<{{2, 4, 6}}>可能与已知的GCC bug 57905有关。我提交了一个新的GCC bug 111277。铿锵拒绝了。

然而,这看起来是标准中的一个缺陷,如CWG 2450.braced -init-list as a template-argument中所示。该问题目前处于起草状态,这意味着已达成非正式共识,但尚未有解决该问题的准确措辞。

进一步说明

您建议的变量模板非常具体,可以概括为适用于所有类型的常量。也不需要创建 type 的变量模板std::integral_constantstd::integral_constant例如,仅在创建类型别名模板时才需要。

你可以写:

template <auto value>
inline constexpr decltype(value) constant;
Run Code Online (Sandbox Code Playgroud)

注意:C++ 提案中已建议使用std::constant与此类似的类型别名,但未成功。

如果你无论如何都不会写arr<{2, 4, 6}>,那么你也可以使用constant上面的变量模板,比如constant<std::array{2, 4, 6}>. 这在语法上是有效的,因为std::array{2, 5, 6}它是一个有效的后缀表达式,由简单类型说明符花括号初始化列表组成。

替代解决方案

如果你想要的只是类似的东西for (int x : arr<{{2,4,6}}>),你也可以写

  • for (int x : {2, 4, 6})这依赖于std::initializer_list
  • for (int x : arr<int>{2, 4, 6})arr别名模板在哪里template <typename T> using arr = T[]
  • for (int x : std::array{2, 4, 6})

变量模板的优点是它是静态的constexpr并且具有静态存储持续时间。但是,这对于小型数组(例如具有三个元素的数组)来说并不重要。

  • 这是一个标准的[监督](https://cplusplus.github.io/CWG/issues/2450.html);GCC 在这里_应该_是正确的。 (2认同)
  • 提到的编译器错误是指在模板声明期间使用的大括号列表作为默认参数值,但这里它是在模板实例化的上下文中,所以我相信这是一种不同的情况。是的,我在这里主要作为一个例子,虽然我在代码中使用的类派生自它,因为它满足我的所有要求,我用它来包装编译期间已知的结构类型的值并传递它们作为几种不同上下文的类型,主要涉及 boost::hana 的操作。 (2认同)