Kyle Strand
Kyle Strand

Reputation: 16509

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

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

Answers (2)

T.C.
T.C.

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

Chris Beck
Chris Beck

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

Related Questions