0
#include <array> #include <tuple> typedef std::tuple<const int> TupleType; constexpr std::array<const int, 2> a = {1, 2}; constexpr void foo() { for (std::size_t i = 0; i < a.size(); ++i) { const int j = i; typedef std::tuple_element<j, TupleType> T; } } 

The code can not be compiled by gcc-7.2 with --std=c++17 with the following compilation error:

error: the value of 'j' is not usable in a constant expression note: in template argument for type 'long unsigned int' 

If we assume that function (and the corresponding loop) is evaluated in compile-time (which is viable for loops starting from c++14), why then this code can not be compiled as far as, even though i is not declared as const, it can actually be constexpr as all of its values are known at compile-time as well.

Could you please clarify whether this code is invalid by its very idea? Or there is a compiler limitation? Or none of the following?

3 Answers 3

2

Could you please clarify whether this code is invalid by its very idea?

It is - you are trying to use a mutable and stateful iteration variable as a constant expression. The whole concept of a constant expression revolves around immutability. It doesn't matter if the loop is executed during compilation.

What you actually should do here is generate code for the following snippet:

{ typedef std::tuple_element<j, TupleType> T; // ... } 

Where j is a placeholder for a constant expression. Here's a possible way of doing it:

template <typename F, typename... Ts> constexpr void for_each_arg(F&& f, Ts&&... xs) { (f(std::forward<Ts>(xs)), ...); } constexpr void foo() { for_each_arg([](auto c) { typedef std::tuple_element<c, TupleType> T; }, std::integral_constant<int, 1>{}, std::integral_constant<int, 2>{}); } 

live example on wandbox

Note that higher-level abstractions over for_each_arg could be easily provided (e.g. iterate over a compile-time range of numbers, or convert a constexpr array to a sequence of integral_constant and iterate over that instead).

Sign up to request clarification or add additional context in comments.

Comments

1

The compiler is right. i and jand not constexpr. Look for yourself:

// v--- not constexpr for (std::size_t i = 0; i < a.size(); ++i) { // Not constexpr either const int j = i; typedef std::tuple_element<j, TupleType> T; } 

If you try to mark j constexpr, you'd see that since i is not, it cannot be so.

If you try to declare i constexpr, you will see that constexpr variables are subject to the same rule as any constexpr variable: you cannot mutate them.

So how can you loop over numbers to generate the types?

You can use pack expansion with index sequences:

template<typename T, T... S, typename F> void for_sequence(std::integer_sequence<S...>, F f) { using unpack = int[]; (void) unpack{(f(std::integral_constant<T, S>{}), void(), 0)..., 0}; } constexpr void foo() { for_sequence(std::make_index_sequence<a.size()>{}, [](auto i) { typedef std::tuple_element<i, TupleType> T; }); } 

Comments

0

Every constexpr function must be able to be evaluated at runtime.

constexpr does not mean "must run at compile time", it means "could possibly be run at compile time".

There is no fundamantal reason why you could not have a constexpr for loop that made the index a constexpr value on each iteration. But C++ does not have that.

It does have a constexpr if which is similar in spirit to what you want.

Until it gets that, you have to write your own.

template<std::size_t...Is> constexpr auto index_over( std::index_sequence<Is...> ){ return [](auto&& f){ return decltype(f)(f)( std::integral_constant<std::size_t, Is >{}... ); }; } template<std::size_t N> constexpr auto index_upto( std::integral_constant<std::size_t,N> ={} ){ return index_over( std::make_index_sequence<N>{} ); } template<class F> constexpr auto foreacher(F&&f){ return [f](auto&&...args){ ( (void)(f(decltype(args)(args)), ... ); }; } constexpr void foo() { index_upto<a.size()>()(foreacher([](auto I){ typedef std::tuple_element<I, TupleType> T; }); } 

is an uncompiled exampke in C++17 (mostly 17 because it has constexpr lambdas).

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.