3

possibly silly question: I am trying to do the following

template<unsigned int N>
class Foo{...}; // Define class Foo accordingly

int main(){
  for (unsigned int i = 0; i < 10; i++){
    Foo<i> f(3);
    f.show();
  }
  return 0;
}

As you can imagine, it won't compile since the variable i is not const. I know the reason for this is that the values assigned inside the non-type template argument Foo<i> must be known at compile time and since this is not the case here, it doesn't really know what to do. Now, I was wondering if there is a way to overcome this problem. First idea is, of course, to declare unsigned int N as member-variable of class Foo.

So question: Is it possible to achieve the desired behaviour above using template arguments, or do I have to declare unsigned int N as member variable of my class?

P.S.: I've tried to find a related to question, but the questions I've found were related to how to use non-type template parameters, where they didn't quite answer the question. Since they didn't mention that this is possible I assume this can't be done...

Edit.

Is it possible to do the following?

template<unsigned int N>
class Foo{...}; // Define class Foo accordingly

int main(){
  std::vector<Foo> v; // I know it's better with shared_ptr, etc.. but to get the idea..
  for (unsigned int i = 0; i < 10; i++){
    Foo<i> f(3);
    f.show();
    v.push_back( f );
  }
  return 0;
}

Evg
  • 25,259
  • 5
  • 41
  • 83
ro_go
  • 90
  • 7
  • @Evg Sorry, didn't mean to. Thanks for making it clear. – ro_go Dec 17 '19 at 09:21
  • 2
    Related: [How to have a const variable in a for loop for the generation of template classes?](https://stackoverflow.com/questions/59200748/how-to-have-a-const-variable-in-a-for-loop-for-the-generation-of-template-classe) – JeJo Dec 17 '19 at 09:42
  • @JeJo, tried to find that one. Thanks for finding it! Still, the container question is not answered. – ro_go Dec 17 '19 at 09:53

1 Answers1

4

We don't have for constexpr in the language, so you can't do it directly. You have to somehow emulate a compile-time for loop. There are several options.

  1. Use std::integer_sequence (C++14) and int... pack:

    template<int i>
    void foo() {
        Foo<i> f(3);
        f.show();
    }
    
    template<int... is>
    void foo(std::integer_sequence<int, is...>) {
       (foo<is>(), ...);  // expands into foo<0>(), foo<1>(), ..., foo<9>()
    }
    
    foo(std::make_integer_sequence<unsigned int, 10>{});
    
  2. Use recursion and if constexpr (C++17) to emulate a for loop:

    template<unsigned int i>
    void foo()
    {
        Foo<i> f(3);
        f.show();
    
        if constexpr (i + 1 < 10)
            foo<i + 1>();
    }
    
    foo<0>();
    
  3. Use std::integral_constant (C++11) and function overloading:

    void foo(std::integral_constant<unsigned int, 10>) {}
    
    template<unsigned int i>
    void foo(std::integral_constant<unsigned int, i>) {
        Foo<i> f(3);
        f.show();
        foo(std::integral_constant<unsigned int, i + 1>{});
    }
    
    foo(std::integral_constant<unsigned int, 0>{});
    

Foo<i> and Foo<j> are distinct types for distinct i and j. You can't put distinct types into a std::vector. If is are known at compile-time, you can make a std::tuple of Foo<i>s. But this still would employ some template tricks.

Evg
  • 25,259
  • 5
  • 41
  • 83
  • Hmm. This seems to be pretty complicated. Is it possible to store each instance of class Foo inside of a container (std::vector)? Because it seems like your solution is just calling the function show resp. the constructor for different values of i, which is understandable since I didn't state otherwise. – ro_go Dec 17 '19 at 09:14
  • @ro_go, `Foo`s are distinct types for different `i`s. You can't store different types in a `std::vector`. – Evg Dec 17 '19 at 09:16
  • Okay, I should have guessed that you can't put different types into std::vector. Using your first suggestion, how do I get the single elements from the sequence (foo(b), ...)? – ro_go Dec 17 '19 at 09:36
  • 2
    `(foo, ...)` is a sequence of function calls. If you want to put `Foo` into a container to access them later, you can use `std::tuple`. – Evg Dec 17 '19 at 09:38
  • So, I guess that using member declarations is easier and more straightforward? Is there any throwback to it? – ro_go Dec 17 '19 at 09:42
  • @ro_go, if you can make `N` a data member, do it. But this is not always possible. Sometimes you need a template parameter (e.g. to have a data member `std::array bar_`), and then you have to resort to template tricks to emulate loops. – Evg Dec 17 '19 at 09:48
  • I do not neccessarily need ```N``` as template, but as I am working with basis functions, I thought to give the i-th basis function the template parameter i. Storing it as member variable won't hurt, at the moment – ro_go Dec 17 '19 at 10:40