在下面的独立程序中,我本希望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
在不迭代的情况下完成,事实上,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
。