1

I am writing a constexpr function and need to use a constexpr for. The loop can be manually expanded out by hand but I find that ugly in code and redundant.

How can I make a "constexpr for"?

Should I make a helper class? If so, how would I go about writing something like this:

#define for_constexpr( TYPE, VAR, START, CONDITION, END_OP, BODY ) \
    for_constexpr_helper< \
        TYPE, START, // TYPE START = 0; \
        [ ]( TYPE VAR ) constexpr { return CONDITION; }, \
        [ ] constexpr { END_OP; }, \
        [ & ]( TYPE VAR ) constexpr { BODY; } >( )

Where the usage is something like

int x = 0;
for_constexpr( int, i, 0, i < 3, ++i, x += i * 2 );

More specifically, how can I use i in a constexpr context, such as a template parameter?

What are my options?

Example code:

auto ret = 0;
for ( int i = 0; i < 3; ++i )
{
    static_assert( i != 4 ); // just an example
    ret += i;
}
return ret;

This fails to be constexpr. Ugly example:

auto ret = 0;
ret += 1;
ret += 2;
ret += 3;
return ret; // works
9
  • 1
    I don't follow; can you show an example of the ugly-by-hand version? Commented May 4, 2020 at 19:02
  • 3
    constexpr functions can already use for loops. What do you need out of it that it isn't giving you? Commented May 4, 2020 at 19:03
  • @NathanOliver using i as a template parameter; Commented May 4, 2020 at 19:04
  • @cigien added some example code Commented May 4, 2020 at 19:05
  • godbolt.org/z/C4mtzB Commented May 4, 2020 at 19:13

1 Answer 1

1

I would not use macros to do this. If you are fine with "passing" the loop variable via

template <std::size_t i>
struct foo{
    constexpr void operator()() {
        static_assert(i != 3);
    }
};

Ie. put the body of your static loop inside the operator() then you can use the following:

template<template <std::size_t> class F, std::size_t... I>
auto static_for_impl(std::index_sequence<I...>)
{
    (F<I>()(),...);
}


template<std::size_t N,template <std::size_t> class F,typename Indices = std::make_index_sequence<N>>
constexpr void static_for(){
    static_for_impl<F>(Indices{});
}

Usage:

int main() {    
    static_for<5,foo>();    
}

Live Example

Alternatively you can use std::integral_constant<std::size_t, N> as argument, so that it works with lambdas and does not require to write a class template (credit goes to @Artyer):

#include <cstddef>
#include <utility>

template<typename F, std::size_t... I>
auto static_for_impl(F&& f, std::index_sequence<I...>) {
    (static_cast<void>(f(std::integral_constant<std::size_t, I>{})), ...);
}

template<std::size_t N, typename F>
constexpr void static_for(F&& f) {
    static_for_impl<F>(std::forward<F>(f), std::make_index_sequence<N>{});
}

int main() {
    static_for<5>([](auto i) {
        static_assert(i != 5);
    });
}

Live Example

While in C++20 you can use a template lambda (credit goes to @Jarod42):

#include <functional>
#include <utility>

template<std::size_t N, typename F>
constexpr void static_for(F&& f) {
    [&f]<std::size_t...Is>(std::index_sequence<Is...>){
        (static_cast<void>(f(std::integral_constant<std::size_t, Is>{})), ...);
     }(std::make_index_sequence<N>{});
}

int main() {
    static_for<5>([](auto i) {
        static_assert(i != 5);
    });
}

Live Example

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

5 Comments

Or alternatively calling with std::integral_constant<std::size_t, N> arguments, so you can use lambdas instead of template classes: godbolt.org/z/Ezw8tQ
@Artyer ah nice. Thats what I wanted but I didnt know how to get there. I will add it to the answer, but only tomorrow
@Artyer: Demo with C++20 template lambda.
@Jarod42 hope you don't mind that I added your code to the answer
@Artyer I added your code to the answer. It is much nicer that my original, but still I left it in just to show different ways. I can imagine cases where the need for a class template isnt really a restriction

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.