constexpr-ness of std::initializer_list::size() vs std::array::size()

孤者浪人 提交于 2021-01-27 18:38:10

问题


The size() member functions of std::initializer_list and std::array have identical signatures:

constexpr size_type size() const noexcept;

Both are constexpr. However, std::array::size() can be used in constexpr context, but std::initializer_list::size() can't:

std::initializer_list<int> il{1, 2, 3, 4};
constexpr std::size_t il_size = il.size();       // (1) - fails with GCC and Clang (*)

std::array<int, 4> arr{1, 2, 3, 4};
constexpr std::size_t arr_size = arr.size();     // (2) - OK

(*) The error is:

in 'constexpr' expansion of 'il.std::initializer_list<int>::size()'
error: the value of 'il' is not usable in a constant expression

As far as I understand, the fact that (1) fails and (2) succeeds is perfectly reasonable because constexpr member function of a class template may fail to satisfy constexpr requirements.

I have two related questions:

  1. Why isn't std::initializer_list implemented in a way such that (1) compiles? Is there something in the standard that prevents such an implementation?
  2. Given that (1) fails, what is the purpose of marking std::initializer_list::size() as constexpr? The only use case seems to be this one:

    constexpr std::initializer_list<int> il{1, 2, 3, 4};   // note constexpr
    constexpr std::size_t il_size = il.size();
    

回答1:


Why isn't std::initializer_list implemented in a way such that (1) compiles? Is there something in the standard that prevents such an implementation?

Yes, it's impossible. An initializer_list can have any size, you cannot during constant evaluation time get the size of an arbitrary runtime initializer_list. This is quite different from std::array, where a given std::array<T, N> has size N. One's size is variable, the other's is fixed.

This isn't really different from any other variable:

struct X { int i; };

X x{42};
constexpr X cx{17};

constexpr int i = x.i;   // error
constexpr int ci = cx.i; // ok

Given that (1) fails, what is the purpose of marking std::initializer_list::size() as constexpr? The only use case seems to be this one

This is not the only use case, far from. constexpr member functions do not just permit you to invoke them on constexpr objects. They more generally permit you to invoke them anywhere during constant evaluation time.

That is, during any kind of constant evaluation, if you create an initializer_list, you can then use its size. A silly minimal example might be:

constexpr size_t four() {
    std::initializer_list<int> lst = {1, 2, 3, 4};
    return lst.size();
}

static_assert(four() == 4);

Note that lst itself is not a constexpr object, it's just some ephemeral thing that got created during the course of evaluating the call to four() during constant evaluation. But we still need that size() to be constexpr - invoking any non-constexpr function is a no-no.

From here you can extend it outwards to any arbitrary code you might want to run that at some point, during constant evaluation, wants to determine the size of an std::initializer_list.




回答2:


It is not that std::initializer_list::size cannot be used in a constant expression. For example, the following probably (see footnote) should compile:

#include <initializer_list>
int main() {
    constexpr int x = (std::initializer_list<int>{1, 2, 3, 4}).size();
    static_assert(x == 4);
}

However, the way you have called it, what presumably happens is that the size method has to perform an lvalue-to-rvalue conversion on a member of the std::initializer_list object and that violates the constraints on constant expressions (C++17 [expr.const]/(2.7)).

In my example, the corresponding lvalue-to-rvalue conversion occurs on "a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;", which makes it allowed (C++17 [expr.const]/(2.7.4)).

In the std::array example, there is probably no lvalue-to-rvalue conversion, since the template parameter is returned.

Footnote: See LWG 2833.



来源:https://stackoverflow.com/questions/59058271/constexpr-ness-of-stdinitializer-listsize-vs-stdarraysize

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!