在constexpr构造函数中初始化数组的合法性?


11

以下代码合法吗?

template <int N>
class foo {
public:
    constexpr foo()
    {
        for (int i = 0; i < N; ++i) {
            v_[i] = i;
        }
    }

private:
    int v_[N];
};

constexpr foo<5> bar;

Clang接受它,但是GCC和MSVC拒绝它。

GCC的错误是:

main.cpp:15:18: error: 'constexpr foo<N>::foo() [with int N = 5]' called in a constant expression
   15 | constexpr foo<5> bar;
      |                  ^~~
main.cpp:4:15: note: 'constexpr foo<N>::foo() [with int N = 5]' is not usable as a 'constexpr' function because:
    4 |     constexpr foo()
      |               ^~~
main.cpp:4:15: error: member 'foo<5>::v_' must be initialized by mem-initializer in 'constexpr' constructor
main.cpp:12:9: note: declared here
   12 |     int v_[N];
      |         ^~

如果这种代码可以,我可以减少使用index_sequences的次数。


1
Gcc10也接受它。
songyuanyao

您可以记录来自MSVC的错误吗?
max66

...以及GCC。
EVG

1
@songyuanyao-g ++ 10接受它编译C ++ 20; 拒绝编译C ++ 17或更早版本;这一点似乎_v应该在初始化列表中进行初始化,直到C ++ 17。也许在C ++ 20中有所改变。
max66

2
@Evg这实际上很有趣,因为它可能表明Clang使用其“意识”将静态存储持续时间对象清零以表示“好的,该对象可能已被默认初始化,但从其int成员读取的内容永远不会有未定义的行为” ”。我想知道GCC是否遵守该规定,或者
反过来

Answers:


13

C ++ 20之前,在constexpr上下文中禁止进行琐碎的默认初始化。

我猜想,原因是很容易“意外地”从默认初始化的原语中读取内容,该行为使程序具有未定义的行为,并且带有未定义行为的表达式被禁止直接使用constexprref)。但是,该语言已经扩展,因此现在编译器必须检查是否发生了这种读取,如果没有发生,则应接受默认初始化。对于编译器来说,这需要更多的工作,但是(如您所见!)对于程序员而言具有实质性的好处。

本文提出在constexpr上下文中允许对琐碎的默认可构造类型进行默认初始化,同时继续禁止调用未定义的行为。简而言之,只要不读取未初始化的值,在堆分配和堆栈分配的场景中,都应在constexpr中允许此类状态。

从C ++ 20开始,v_像您一样保留“未初始化” 是合法的。然后,您继续为其分配所有元素值,这很棒。


4
@ max66我也是!我所做的就是扫描Wikipedia上的C ++ 20更改列表,找到与之相关的内容constexpr,并略过链接的建议;)
在轨道上进行的Lightness Races

3
糟糕的是,我使用C ++已经超过20年了。如果每天我学习新东西...或者我是一个不好的程序员,或者C ++变得太复杂了。
max66

5
@ max66几乎可以肯定是后者。此外,它每两年都会从根本上变化,这使其成为一个快速发展的目标。谁能跟得上?!甚至编译器也跟不上这个。
在轨道进行的轻度比赛

@ max66本文浮现在脑海:记住瓦萨!
EVG

@Evg噢,哇,那张纸已经通过了(讽刺)。发现!
在轨道进行轻度比赛
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.