Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected non-constant std::initializer_list

I was toying a little bit with the indices trick to see where I could go to with and came across a strange error... First, the plain not-so-old indices:

template<std::size_t...>
struct indices {};

template<std::size_t N, std::size_t... Indices>
struct make_indices:
    make_indices<N-1, N-1, Indices...>
{};

template<std::size_t... Indices>
struct make_indices<0, Indices...>:
    indices<Indices...>
{};

I created a compile-time array class derived from a std::initializer_list and had it indexable (assume that N3471 is support by your compiler. It will be in the next standard anyway). Here it is:

template<typename T>
struct array:
    public std::initializer_list<T>
{
    constexpr array(std::initializer_list<T> values):
        std::initializer_list<T>(values)
    {}

    constexpr auto operator[](std::size_t n)
        -> T
    {
        return this->begin()[n];
    }
};

So, I tried to create a function that returns a copy of an array after having added 1 to each of its members:

template<typename T, std::size_t... I>
auto constexpr add_one(const array<T>& a, indices<I...>)
    -> const array<T>
{
    return { (a[I]+1)... };
}

And to finish with the code, here is my main:

int main()
{
    constexpr array<int> a = { 1, 2, 3 };
    constexpr auto b = add_one(a, make_indices<a.size()>());

    return 0;
}

I did not think that code would compile anyway, but I am quite surprised by the error message (Here is the ideone code):

In function 'int main()':
error: 'const smath::array<int>{std::initializer_list<int>{((const int*)(& const int [3]{2, 3, 4})), 3u}}' is not a constant expression

So, could someone explain to me what exactly is not constant enough for the compiler in the above code?

EDIT: Follow-ups for that question

  • Is it legal to declare a constexpr std::initializer_list object?
  • Confusion about constant expression
like image 498
Morwenn Avatar asked Apr 15 '13 19:04

Morwenn


People also ask

What is the problem with initializer_list?

The main problem with std::initializer_list is probably the quirks regarding uniform initialization. But this can be solved easily: add an extra layer of indirection, i.e. define your own initializer_list: This is just a wrapper over std::initializer_list .

What is initializer_list in C++?

(not to be confused with member initializer list ) An object of type std::initializer_list<T> is a lightweight proxy object that provides access to an array of objects of type const T.

How to return an empty list from an initializer_list?

A pointer to the first element of the initializer_list. If the list is empty, the pointer is the same for the beginning and end of the list. Returns a pointer to one past the last element in an initializer list. A pointer to one past the last element in the list. If the list is empty, it's the same as the pointer to the first element in the list.

Why can’t I call an initializer list from a template?

Even though the core language has been amended for std::initializer_list , the expression {1, 2, 3, 4, 5} does not have the type std::initializer_list<int> . So if you have a template function: And you want to call it with an initializer list: You’ll get an error. This makes generic make function more complicated, because that won’t compile:


1 Answers

From: the man himself http://www.stroustrup.com/sac10-constexpr.pdf

Specifically: its return type, and the types of its parameters (if any), are literal types (see x2.2). For concreteness, literal types include bool, int, or double; its body is a compound statement of the form { return expr; } where expr is such that if arbitrary constant expressions of appropriate types are substituted for the parameters in expr, then the resulting expression is a constant expression as defined in introductory paragraph of x2. The expression expr is called a potential constant expression.

like image 105
Seth Hays Avatar answered Sep 22 '22 02:09

Seth Hays