Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterate with constexpr

Tags:

c++

c++11

I'd like to write something like that :

template<int i> void f() {}

for (constexpr int i : {1,2,3})
{
    f<i>();
}

Is it possible to iterate on constexpr ?

Thank you

like image 512
Violette Avatar asked Apr 04 '17 09:04

Violette


2 Answers

As you probably understand, you cannot do the like of:

for (constexpr int i : {1,2,3})
{
    f<i>();
}

because, if i is to vary from 1 through 3 in a loop, then it is a variable and not a compiletime constant. And a variable cannot be a template argument, as in f<i>: only a compiletime constant can be a template argument.

In C++11 and later, thanks to variadic templates, you can effectively iterate over an arbitary sequence of compiletime constants by using compiletime recursion of a template function that accepts a suitable arbitrary sequence of template arguments.

That will hardly mean anything to you if you don't already know how to do it. Here is a C++11 example that does what you want to express:

#include <type_traits>
#include <iostream>

template<int i> void f()
{
    std::cout << i << '\n';
}

// This overload is chosen when there is only 1 template argument.
template<int First, int ...Rest>
typename std::enable_if<sizeof...(Rest) == 0>::type
for_each_f()
{
    f<First>();
}

// This overload is chosen when there is > 1 template argument.
template<int First, int ...Rest>
typename std::enable_if<sizeof...(Rest) != 0>::type
for_each_f()
{
    f<First>();
    for_each_f<Rest...>();
}

int main()
{
    for_each_f<2,3,5,7,11>();
    return 0;
}

See it live

Besides variadic templates, this technique depends on the very important C++ meta-programming principle of SFINAE, and on std::enable_if, which is the tool that the Standard C++ library provides for exploiting SFINAE.

101010's answer demonstrates a more sophisticated and powerful style of solution that is available in C++14 (and easy enough to implement in C++11 if you write some supporting boilerplate).

like image 158
Mike Kinghan Avatar answered Oct 14 '22 15:10

Mike Kinghan


No you can't use a for loop to iterate over at compile time. The for control structure in C++ is used for runtime control flow.

However, you could use other compile time facilities. For example in C++ 14 you could achieve what you want in the following manner:

  1. Define a template wrapper class that's gonna call your function.

    template<int i>
    struct wrapper {
      void operator()() const { f<i>(); }
    };
    
  2. Use std::index_sequence to generate compile time indices.

    template<template<int> class W, std::size_t... I>
    void caller_impl(std::index_sequence<I...>) {
      int t[] = { 0, ((void)W<I>()(), 1)... };
      (void) t;
    }  
    
    template<template<int> class W, std::size_t N, typename Indices = std::make_index_sequence<N>>
    void call_times() {
      caller_impl<W>(Indices());
    }
    
  3. Then call as

    int main() {
      call_times<wrapper, 42>();
    }
    

    Live Demo

If C++14 is not an option you could take a look here of how you could implement std::index_sequence your self.

like image 45
101010 Avatar answered Oct 14 '22 16:10

101010