Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to static_assert that an initializer list is a certain size

Would it be possible to verify that an initializer list that is being passed to a constexpr constructor is a certain size? Or would this only be possible to do at runtime?

This is the idea, but it doesn't work:

struct group
{
        constexpr group(
            std::initializer_list<std::initializer_list<UINT const> const> groups
        )
        {
            static_assert(each_list_size_greater_than_1(groups.begin(), groups.end()));
        }

        constexpr static bool each_list_size_greater_than_1(
            std::initializer_list<std::initializer_list<UINT const> const>::const_iterator const begin
            , std::initializer_list<std::initializer_list<UINT const> const>::const_iterator const end)
        {
            return begin == end || begin->size() > 1 && each_list_size_greater_than_1(begin + 1, end);
        }
};

I've looked at VS2015's std::initializer_list implementation and begin(), end() and size() are all constexpr functions.

like image 866
Adrian Avatar asked Dec 30 '16 17:12

Adrian


1 Answers

Although the size() of an std::initializer_list<T> can evaluate to a constexpr the size() member won't behave like a constexpr within a constexpr function: it is intentional that objects only behave like constexpr within the constexpr expression where they were introduce but not elsewhere.

For example:

constexpr get_size(std::initializer_list<int> list) {
    constexpr std::size_t csize = list.size(); // ERROR (1)
    std::size_t           size  = list.size(); // OK
    return size;
}

int main() {
    constexpr std::size_t csize = get_size(std::initializer_list<int>{ 1, 2, 3 }); // OK (2)
    // ...
}

In the first case (1) the value which is assumed to yield a constexpr depends on data created before the constexpr starts. As a result, it doesn't evaluate to a constexpr. In the second case (2) the data is defined within the constexpr and, thus, the result can become a constexpr.

I haven't been part of the discussions leading to this design but it seems it is motivated by the desire to prevent constexpr arguments changing the result type of a constexpr function: if the values were constexprs within the function definition, they would also be constexprs in the return value and could, thus, be used as template arguments in the return type. That would lead to different values to constexpr function yielding different types. So far, you can get a different return type only by varying the argument types of a function and/or by changing the number of arguments of a function.

like image 72
Dietmar Kühl Avatar answered Nov 01 '22 19:11

Dietmar Kühl