可以在编译时使用`constexpr std :: initializer_list`但不使用ODR吗?

时间:2016-04-18 20:33:55

标签: c++ c++11 constexpr one-definition-rule

在下面的独立程序中,我原本期望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也会产生类似的错误; clangread 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而无需迭代而完成the begin() method is indeed marked constexpr as of C++14的任何事情。那么,有哪些东西可以通过constexpr std::initializer_list来完成,而且不需要ODR定义吗?

注意:我正在两个编译器上使用-std=c++14进行编译,但我发现它们可能并不完全符合 - 即使版本I& #39;使用相当最近。我有兴趣知道更新的版本是否允许上述代码。

编辑:与ChrisBeck讨论后,改变了我的示例和分析;在他的回答下看到讨论,以及编辑这个问题的历史。

编辑2:根据T.C。的建议,我从constexpr循环中删除for,离开for (auto i : Foo::MyNumbers)。这导致GCC和Clang都出现undefined reference to 'Foo::MyNumbers'链接错误。

2 个答案:

答案 0 :(得分:6)

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

该标准未提及优化选项。相反,不同的编译器应该按照他们认为合适的方式构成不同的优化方案,并且具有很大的余地。 ODR应该确保符合规范的代码将链接到所有符合要求的编译器。 (谢谢C ++标准委员会!)

所以,请不要忘记循环展开以及链接时的含义。唯一重要的是ODR是否使用了ODR。

  

除了循环展开和初始化对象之外,我无法真正想到任何可以在编译时完成的事情

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

答案 1 :(得分:4)

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进行左值到右值转换),因此不能用于初始化{ {1}}变量constexpr