ro_go
ro_go

Reputation: 90

Passing non-type parameter agument for templates

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;
}

Upvotes: 3

Views: 87

Answers (1)

Evg
Evg

Reputation: 26282

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.

Upvotes: 4

Related Questions