Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't the operator[] of a std::array temporary constexpr?

I was stuffing some values into a constexpr std::array and then continuing the compile-time static goodness into more constexpr values when I discovered that you can't use an element as a constexpr initializer in C++11.

This is because std::array::operator[] is actually not marked constexpr until C++14: https://stackoverflow.com/a/26741152/688724

After a compiler flag upgrade, I can now use an element of a constexpr std::array as a constexpr value:

#include <array>

constexpr std::array<int, 1> array{{3}};
// Initialize a constexpr from an array member through its const operator[]
// that (maybe?) returns a const int & and is constexpr
constexpr int a = array[0];  // Works in >=C++14 but not in C++11

But sometimes I want to use a temporary array in a constexpr computation, and that doesn't work.

// Initialize a constexpr from a temporary
constexpr int b = std::array<int, 1>{{3}}[0];  // Doesn't work!

I get this from clang++ 3.6 with -std=c++14:

prog.cc:9:15: error: constexpr variable 'b' must be initialized by a constant expression
constexpr int b = std::array<int, 1>{{3}}[0];  // Doesn't work!
              ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~
prog.cc:9:19: note: non-constexpr function 'operator[]' cannot be used in a constant expression
constexpr int b = std::array<int, 1>{{3}}[0];  // Doesn't work!
                  ^
/usr/local/libcxx-3.5/include/c++/v1/array:183:41: note: declared here
    _LIBCPP_INLINE_VISIBILITY reference operator[](size_type __n)             {return __elems_[__n];}
                                        ^
1 error generated.

What's the difference between the two variables I'm indexing into? Why can't I use a direct-initialized temporary std::array's operator[] as constexpr?

like image 799
Xo Wang Avatar asked Jul 29 '15 01:07

Xo Wang


2 Answers

The temporary array in your second example is itself not const, so you end up calling the non-const operator[] overload, which is not constexpr. You can get your code to work if you first cast the array to const.

constexpr int b = static_cast<std::array<int, 1> const&>(std::array<int, 1>{{3}})[0];

Live demo

like image 183
Praetorian Avatar answered Nov 16 '22 00:11

Praetorian


Alternatively to @Praetorian's workaround, you can use std::get(std::array)

#include<array>
int main(){
    constexpr int b = 
    //  std::array<int, 1>{{3}}[0]; // Doesn't work!
    //  static_cast<std::array<int, 1> const&>(std::array<int, 1>{{3}})[0]; // long but Works!
        std::get<0>(std::array<int, 1>{{3}});// Works!
}

I think std::get is more "agressive" than operator[] in producing a constexpr.

(Tested with clang 3.5 and gcc 5.0 C++14, should work with C++11)

Also, for some reason (related to the template parameter), ADL doesn't work here, so it is not possible to just write get<0>(std::array<int, 1>{{3}}).

like image 32
alfC Avatar answered Nov 16 '22 00:11

alfC