是否可以在编译时使用`constexpr std::initializer_list`,但不能使用ODR

Can a `constexpr std::initializer_list` be used at compile time but not ODR-used?

本文关键字:initializer list ODR 但不能 std 是否 constexpr 编译      更新时间:2023-10-16

在下面的独立程序中,我本希望for循环在编译时展开,甚至完全计算出来,从而在链接时不需要Foo::MyNumbers

struct Foo
{
  constexpr static auto MyNumbers =
  {
    3,
    28,
    200,
  };
};
constexpr int getSum(void)
{
  auto sum = 0;
  for (constexpr auto i : Foo::MyNumbers)
  {
    sum += i;
  }
  return sum;
}

然而,即使使用-O3clang++ 3.7(从之前的源代码构建到3.7版本)和g++ 5.1都给出了类似的错误;clang表示read of non-constexpr variable '__begin' is not allowed in a constant expression,而g++表示the value of '__for_begin' is not usable in a constant expression

我真的想不出有什么东西可以在编译时用std::initizlizer_list在不迭代的情况下完成,事实上,begin()方法在C++14中确实标记为constexpr。那么,constexpr std::initializer_list有什么不需要ODR定义的有用功能吗?

注意:我在这两个编译器上都使用-std=c++14进行编译,但我意识到它们可能并不完全一致——尽管我使用的版本是相当新的。我很想知道更多的最新版本是否允许上面的代码。

编辑:在与ChrisBeck讨论后更改了我的示例和分析;请参阅他的答案下的讨论,以及编辑这个问题的历史。

第2版:在T.C.的建议下,我从for循环中删除了constexpr,留下了for (auto i : Foo::MyNumbers)。这导致GCC和Clang都出现undefined reference to 'Foo::MyNumbers'链路错误。

ODR不依赖于优化选项。ODR是标准的一部分。

该标准未提及优化选项。相反,不同的编译器应该组成他们认为合适的不同优化方案,并且在这方面有很大的余地。ODR应该确保一致性代码将链接到所有一致性编译器上。(感谢C++标准委员会!)

所以,不要去想循环展开,以及这在链接时意味着什么。对ODR来说,唯一重要的是某个东西是否使用了ODR。

我真的想不出在编译时能完成什么有用的事情。。。除了循环展开和初始化对象

由于C++14std::initializer_list是一个文字类型。因此,您可以在编译时计算中轻松使用它。使用std::initializer_list初始化具有constexpr构造函数的对象在某些代码库中非常常见。

for (constexpr auto i : Foo::MyNumbers)
{
  sum += i;
}

这是无效的,无论包含它的函数是否为constexpr,因为它扩展到

{
    auto&& __range = Foo::MyNumbers;
    for(auto __begin = __range.begin(), __end = __range.end();
        __begin != __end;
        ++__begin) {
        constexpr auto i = *__begin;
        sum += i;
    }
}

*__begin显然不是一个常量表达式(它需要对非constexpr变量__begin进行左值到右值的转换),因此不能用于初始化constexpr变量i