在下面的独立程序中,我原本期望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;
}
但是,即使使用-O3
,clang++
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
而无需迭代而完成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'
链接错误。
答案 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
。