模板化的constexpr变量

8

我想确认一下这段代码是否符合C++17的标准。

#include <iostream>

template<int N> inline constexpr float MyConst;

template<> inline constexpr float MyConst<1> = 1.1f;
template<> inline constexpr float MyConst<2> = 2.2f;

int main ()
{
    std::cout << MyConst<1> << '\n';

    return 0;
}

如果使用g++MSVC编译,我不会收到错误信息(并且可以得到正确的输出结果)。

但是,Intelclang会报错:

test.cpp(3): error: missing initializer for constexpr variable
  template<int N> inline constexpr float MyConst;
                         ^

使用-std=c++17进行编译(对于MSVC,使用/std:c++17)。

在godbolt上尝试了最新的编译器,并在本地机器上测试过。


gcc编译没有问题,所以可能是clang的一个bug? - Moia
那看起来就像是我尝试过的所有其他编译器中的一个错误。 - user2052436
1
MSVC 接受您的代码。 - Adrian Mole
@AdrianMole 很好知道 - 我在使用Linux。 - user2052436
@user2052436 我也使用Linux,对于gcc没有问题。在coliru上使用g++可以运行,但是使用clang会失败。你应该尝试在godbolt上运行。 - Moia
显示剩余2条评论
3个回答

3
一个 constexpr(字面常量)变量必须立即初始化。因此,MyConst 的模板需要一个初始值/定义。GCC违反规范,在第一次出现时不要求定义。如果使用变量的非特化形式,例如MyConst<3> ,则会从GCC中得到类似的错误
<source>: In instantiation of 'constexpr const float MyConst<3>':
<source>:10:18:   required from here
<source>:3:40: error: uninitialized 'const MyConst<3>' [-fpermissive]
    3 | template<int N> inline constexpr float MyConst;
      |                                        ^~~~~~~
ASM generation compiler returned: 1
<source>: In instantiation of 'constexpr const float MyConst<3>':
<source>:10:18:   required from here
<source>:3:40: error: uninitialized 'const MyConst<3>' [-fpermissive]
    3 | template<int N> inline constexpr float MyConst;
      |                            

这可以通过提供MyConst的初始定义来解决,例如:
// Use a "sensible default"
template<int N> inline constexpr float MyConst(0.0f);

// Provide a more general definition
template<int N> inline constexpr float MyConst = N*1.1f;

有关标准的相关部分,请参见 dcl.constexpr第1段

constexpr限定符只能应用于变量或变量模板的定义,或者函数或函数模板的声明。consteval限定符仅适用于函数或函数模板的声明。使用constexpr或consteval限定符声明的函数或静态数据成员隐式地成为内联函数或变量。如果任何一个函数或函数模板的声明具有constexpr或consteval限定符,则其所有声明都必须包含相同的限定符。


我理解constexpr需要被初始化。我的期望是,使用模板化的constexpr时,它会以SFINAE方式工作,也就是说只要我仅使用MyConst<1>MyConst<2>,而不是例如MyConst<3>,那么它就可以正常工作。 - user2052436
我的情况也是如此,但初始声明的 constexpr 性质阻止了这一点。您可以将原始声明更改为非 constexpr,并使模板特化 constexpr,这样它就会表现得更直观。 https://godbolt.org/z/1MbYKbGva - user213305
谢谢。那是一个好的解决方案。 我看到另一个解决方案是使用带有“static inline constexpr MyConst”的template <int N> struct结构;该“struct”被专门为所需的值N进行了特化。这将导致错误的N值在编译时失败。这种方法将给我我最初想要的,没有任何警告,对吗? - user2052436
如果您尝试这样做,GCC 最终会注意到一个 constexpr 对象的定义缺失,并且无法编译任何示例。https://godbolt.org/z/M67d5fnqd - user213305
1
我指的是这个 https://godbolt.org/z/a9GdYzEsc - user2052436

2
这是DR 1712
一方面,constexpr变量必须被初始化;另一方面,template在实例化之前并不存在,因此不一定需要初始值。
不幸的是,标准目前还没有豁免constexpr变量模板需要有初始值的要求。因此,从技术上讲,该代码是不合法的。
相应的GCC问题是#68012,质疑是否需要进行诊断。

2
问题在于 constexpr 需要被初始化。
根据 C++14 标准第 7.5.1 节第 9 段,

在对象声明中使用 constexpr 指定符将对象声明为 const。这样的对象应该具有字面类型并且应该被初始化。

因此:
#include <iostream>

template<int N> inline constexpr float MyConst = 0.0f;

template<> inline constexpr float MyConst<1> = 1.1f;
template<> inline constexpr float MyConst<2> = 2.2f;

int main ()
{
    std::cout << MyConst<1> << '\n';

    return 0;
}

适用于g++和clang,都能很好地工作。


我理解“constexpr需要被初始化”。我的期望是,使用模板化的constexpr时,它会以SFINAE的方式工作,也就是只要我只使用MyConst<1>MyConst<2>,而不是例如MyConst<3>,那么它就可以正常工作。 - user2052436
正如用户202436所指出的那样:constexpr关键字只能应用于变量或变量模板的定义,或函数或函数模板的声明。而constexpr定义意味着初始化。 - Jean-Baptiste Yunès

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接