According to cppreference.com, std::initializer_lists have constexpr constructors and constexpr size methods (since C++14).
Although the compiler I was using seemed to agree that the size of a constexpr initializer list was indeed constexpr, in some circumstances it didn't believe my list was constexpr. Since std::initializer_lists can involve some "compiler magic," I began wondering whether constexpr doesn't apply to them in quite the same way as it does for non-magical objects.
I hopped onto Compiler Explorer and discovered that the major compilers don't agree on the topic.
So what are the correct behaviors (per the standard) for the four cases below?
#include <initializer_list>
using size_type = std::initializer_list<int>::size_type;
template <typename T>
size_type Foo(std::initializer_list<T> const &list) {
return list.size();
}
int main() {
// 1. Example based on
// https://en.cppreference.com/w/cpp/utility/initializer_list/size
// gcc: works
// clang: no viable c'tor or deduction guide
// msvc: works
static_assert(std::initializer_list{1, 2, 3}.size() == 3);
// 2. Make a constexpr std::initializer_list<T> with T deduced
// gcc: not constant expression
// clang: no viable c'tor or deduction guide
// msvc: works
constexpr auto the_list = std::initializer_list{1, 2, 3};
// 3. Static assert using constexpr size
// gcc: fails because of above
// clang: fails because of above
// msvc: works
static_assert(the_list.size() == 3);
// 4. Extract the size via a constexpr function
// gcc: fails because of above
// clang: fails because of above
// msvc: expression did not evaluate to a constant
constexpr auto the_list_size = Foo(the_list);
return 0;
}
- "gcc" means x86-64 gcc (trunk) with
-std=c++20
(also tested with 13.1) - "clang" means x86-64 clang (trunk) with
-std=c++20
(also tested with 16.0.0) - "msvc" means x64 msvc v19.latest with
/std:c++20
I would have expected all four cases to compile. But some compilers rejected some of them, causing me to reconsider my understanding. Are the compilers rejecting correct code (or accepting incorrect code)? Am I inadvertently relying on implementation-defined behavior? Is the standard ambiguous?
The closest existing question I can find is Assigning a initializer_list to std::array, but the details there are specific to std::array, which is not the case in my examples.
Foo
is not marked asconstexpr
... – Phytohormoneinitializer_list
is a bug, which is tracked in issues/60876. – Surpliceconstexpr auto the_list = std::initializer_list{1, 2, 3};
->constexpr static auto the_list = std::initializer_list{1, 2, 3};
– Fenrirstd::initializer_list
a literal type? – Busywork