7

I have read many questions and answers, but this question caught my eye the most; it and its answers are helpful but I still feel that I do not fully understand the uses and the theories behind using non-type template arguments. They provide many useful examples as to when it is used, but none really shed light on the theories behind the non-type template arguments.

I am interested in knowing not specifically when in examples, but more broadly why I would be inclined to use a non-type template argument over just a regular function argument.

I understand that they are determined at compile-time and that every new call creates a new function with the determined value for the non-type template argument. So why exactly would I want to create many different instances of the same function when I could just input the parameter I want into a function and have the same result - supposedly - with only one compiled function.

In essence, why should I be inclined to do #1, instead of #2, as according to the final section of this page of cplusplus.com?

#1:

template <class T, int N>
T fixed_multiply (T val)
{
  return val * N;
}

int main() {
  std::cout << fixed_multiply<int,2>(10) << '\n';
  std::cout << fixed_multiply<int,3>(10) << '\n';
}

#2:

template <class T>
T fixed_multiply (T val, int N)
{
  return val * N;
}

int main() {
  std::cout << fixed_multiply<int>(10, 2) << '\n';
  std::cout << fixed_multiply<int>(10, 3) << '\n';
}

In addition, would there be any performance benefits or the such of either one? Are there any common applications that could benefit from using non-type template arguments, or is this a highly technical parameter used in specific applications to yield specific results?

Edit: This got marked as a duplicate for some reason, the first paragraph explains my reasons for a similar question.

Community
  • 1
  • 1
Ziyad Edher
  • 2,150
  • 18
  • 31
  • Now use second variant to multiply all elements of vector by 5 using `std::transform` – Revolver_Ocelot Jul 08 '16 at 15:54
  • It doesn't makes much of a difference in the example you show, but if you make both values non-type template arguments there is a big difference because then you can make the templated version a `constexpr` function that is evaluated at compile time. Non-type template arguments are also useful for things like [`std::array`](http://en.cppreference.com/w/cpp/container/array) or functions taking C-style arrays to get an actual array type with size, and not needing to pass the size as a run-time argument. – Some programmer dude Jul 08 '16 at 15:56
  • 2
    @Amit That's the same question he linked to in the first line of the question. – Barmar Jul 08 '16 at 15:58
  • @Barmar - I know that, and I believe that question covers all this question has - don't you agree?? (Or more accurately, if anyone has a valid answer to this question that's not already in the other question, it should be posted there) – Amit Jul 08 '16 at 15:59
  • @Revolver_Ocelot I do not understand what you mean by that, can you elaborate? – Ziyad Edher Jul 08 '16 at 16:00
  • @Amit I don't think it does, I said what I felt that I didn't understand from their explanations on that question, they were giving detailed examples about when to use it, not the theories as to why. – Ziyad Edher Jul 08 '16 at 16:01
  • @Revolver_Ocelot That's still possible with the help of lambdas or `std::bind`. – Some programmer dude Jul 08 '16 at 16:05
  • @Wintro The "why" is because you can do those things better or more easily with template parameters than other techniques. – Barmar Jul 08 '16 at 16:08
  • @JoachimPileborg then you will add another layer of wrappers or use `std::bind`, which is worse. By that logic, type templates are not needed, because you can just use a bunch of overloads. The problem with OP question is that both approaches are viable. One might be preferred over other depending on intended usage. – Revolver_Ocelot Jul 08 '16 at 16:12
  • @Barmar I understand the theory behind being able to have any type of information inputted but I do not really get why it's easier when it's a specified type, for me it would make more sense to input it as a normal parameter than a non-type template parameter. – Ziyad Edher Jul 08 '16 at 16:15
  • In powerful programming languages there are almost always multiple ways to do things, e.g. arrays vs std::vector. – Barmar Jul 08 '16 at 16:15
  • 2
    @Wintro Template parameters can be processed at compile time. So the function with template parameter `2` can generate a left shift instead of a multiply, and the calling sequence only has to pass one parameter. – Barmar Jul 08 '16 at 16:16
  • History is a good reason.. otherwise yes, `integral_constant` and similar constructions could solve it. – lorro Jul 08 '16 at 16:48

1 Answers1

10

They are useful when you want (or need) the compiler to make certain compile-time optimizations while keeping code well-factored. I'll briefly list a few examples:

Branch elimination

void doSomething(bool flag) {
  if (flag) {
    //whatever
  }
}

template <bool flag>
void doSomething() {
 if (flag) {
   //whatever
}

}

In the above example, if you always know the value of flag at compile time when you call doSomething, then you can avoid the cost of a branch without manually creating doSomethingTrue and doSomethingFalse functions which may require you to repeat a lot of code. This trick is useful when you want to factor code between send and recv sides in network code, for example, and your code sits deep down in a performance-critical stack.

Avoid dynamic memory management

void someFunction(size_t size, float* inout) {
  std::vector<float> tmp(size);
  //do something with inout and tmp and then store result in inout
}

template <size_t N>
void someFunction(float *inout) {
  float tmp[N]; //or std::array if you like
  //do something with inout and tmp and store result in inout
}

In the above example, the second version will perform much better if it is stuck inside a critical loop.

Allow special optimizations

Consider the loop:

for (size_t i = 0; i < N; ++i) {
  //some arithmetic
}

If the compiler knows that N is 4 or 8, it may be able to replace your arithmetic with equivalent SIMD instructions, or at least unroll your loop more intelligently than it could if N were a runtime argument. This sort of thing is commonplace in the GPU programming (CUDA) world.

Template meta-programming

When you do some non-trivial code generation with templates, sometimes you must iterate over things at compile time. You need template non-type arguments to be able to do this. Argument packs and std::tuple are one common case, but there are many more, and hard to exemplify here.

To answer your question of when you'd make something a template parameter if you have a choice: code that takes dynamic (runtime) arguments is always more flexible, so you should only convert something to be a template argument if you can justify it with a substantial gain in performance. On the other hand if you find yourself writing much duplicated code over a set of enumerations, you should probably try and factor with templates.

Always Confused
  • 470
  • 4
  • 7