Reputation: 16509
In the following standalone program, I would have expected the for
loop to be unrolled, or even entirely computed, at compile-time, making Foo::MyNumbers
unnecessary at link-time:
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;
}
However, even with -O3
, both clang++
3.7 (build from source prior to the 3.7 release) and g++
5.1 give an similar errors; clang
says read of non-constexpr variable '__begin' is not allowed in a constant expression
, while g++
says the value of '__for_begin' is not usable in a constant expression
.
I can't really think of anything that could usefully be accomplished at compile-time with a std::initizlizer_list
without iterating over it, and indeed the begin()
method is indeed marked constexpr
as of C++14. So is there anything that can usefully be accomplished with a constexpr std::initializer_list
that doesn't require ODR-defining it?
NOTE: I am compiling with -std=c++14
on both compilers, but I realize it's possible that they're not fully conformant--even though the versions I'm using are fairly recent. I'd be interested to know if more up-to-date versions permit the code above.
EDIT: Changed my example and my analysis after discussion with ChrisBeck; see discussion under his answer, as well as edit history for this question.
EDIT 2: At T.C.'s suggestion, I removed constexpr
from the for
loop, leaving for (auto i : Foo::MyNumbers)
. This results in an undefined reference to 'Foo::MyNumbers'
link error with both GCC and Clang.
Upvotes: 2
Views: 2267
Reputation: 137425
for (constexpr auto i : Foo::MyNumbers)
{
sum += i;
}
This is invalid, whether or not the function containing it is constexpr
, because it expands to
{
auto&& __range = Foo::MyNumbers;
for(auto __begin = __range.begin(), __end = __range.end();
__begin != __end;
++__begin) {
constexpr auto i = *__begin;
sum += i;
}
}
*__begin
is plainly not a constant expression (it necessitates an lvalue-to-rvalue conversion on the non-constexpr
variable __begin
), and so cannot be used to initialize the constexpr
variable i
.
Upvotes: 4
Reputation: 16204
ODR does not depend on the optimization options. ODR is part of the standard.
The standard does not make reference to optimization options. Instead, different compilers are supposed to make up different optimization schemes as they see fit, and have lots of leeway in that. The ODR is supposed to ensure that conforming code will link on all conforming compilers. (Thank you C++ standards committee!)
So, put it out of your mind about loop unrolling and what that means at link time. The only thing that matters for ODR is whether something is ODR-used.
I can't really think of anything that could usefully be accomplished at compile-time ... besides loop-unrolling and initializing objects
Since C++14 std::initializer_list
is a literal type. So, you can easily use it in compile-time computations. Initializing objects that have constexpr constructors using a std::initializer_list
is very common in some codebases.
Upvotes: 8