Why isn't a for-loop a compile-time expression?

c++, constexpr

Solution

Here's a way to do it that does not need too much boilerplate, inspired from http://stackoverflow.com/a/26902803/1495627 :

template<std::size_t N>
struct num { static const constexpr auto value = N; };

template <class F, std::size_t... Is>
void for_(F func, std::index_sequence<Is...>)
{
  using expander = int[];
  (void)expander{0, ((void)func(num<Is>{}), 0)...};
}

template <std::size_t N, typename F>
void for_(F func)
{
  for_(func, std::make_index_sequence<N>());
}

Then you can do :

for_<N>([&] (auto i) {      
  std::get<i.value>(t); // do stuff
});

If you have a C++17 compiler accessible, it can be simplified to

template <class F, std::size_t... Is>
void for_(F func, std::index_sequence<Is...>)
{
  (func(num<Is>{}), ...);
}

Problem

If I want to do something like iterate over a tuple, I have to resort to crazy template metaprogramming and template helper specializations. For example, the following program won't work: ``` #include <iostream> #include <tuple> #include <utility> constexpr auto multiple_return_values() { return std::make_tuple(3, 3.14, "pi"); } template <typename T> constexpr void foo(T t) { for (auto i = 0u; i < std::tuple_size<T>::value; ++i) { std::get<i>(t); } } int main() { constexpr auto ret = multiple_return_values(); foo(ret); } ``` Because `i` can't be `const` or we wouldn't be able to implement it. But for loops are a compile-time construct that can be evaluated statically. Compilers are free to remove it, transform it, fold it, unroll it or do whatever they want with it thanks to the as-if rule. But then why can't loops be used in a constexpr manner? There's nothing in this code that needs to be done at "runtime". Compiler optimizations are proof of that. I know that you could potentially modify `i` inside the body of the loop, but the compiler can still be able to detect that. Example: ``` // ...snip... template <typename T> constexpr int foo(T t) { /* Dead code */ for (auto i = 0u; i < std::tuple_size<T>::value; ++i) { } return 42; } int main() { constexpr auto ret = multiple_return_values(); /* No error */ std::array<int, foo(ret)> arr; } ``` Since `std::get<>()` is a compile-time construct, unlike `std::cout.operator<<`, I can't see why it's disallowed.

Original source

Related problems