Many variable arguments

Let's say I have a variable number of arguments that I want to propagate together. The first way that I think is a recursive algorithm:

template<typename Head> u64 Multiply(Head head) const { return head; } template<typename Head, typename... Tail> u64 Multiply(Head head, Tail... tail) const { return head * Multiply(tail...); } 

But then I saw this trick:

 // u32 and u64 are 32 and 64 bit unsigned integers. template<typename... T> u64 Multiply(T... args) { u64 res = 1; for (const u32& arg: {args...}) res *= arg; return res; } 

The second seems to me nicer. Easier to read. However, how does this affect performance? Is anything copied? What does {args...} first? Is there a better way?

I have access to C ++ 14.

Edit to be clear: this is about multiplying runtime, not compilation time.

More to be clear: I do not want to compute integers necessarily (although this is my current application), but the algorithm I found was specialized for integers.

Optional: Arguments of the same type. Algorithms without this restriction would be very interesting, but perhaps for another question.

+7
c ++ templates c ++ 14
source share
2 answers

Some questions are asked here:

  • What is the effect on performance? I do not know. You will need to measure. Depending on the type of arguments, I can imagine that the compiler fully optimizes things anyway: it knows the number of arguments and types.
  • What is { args... } ? Well, it creates std::initializer_list<T> for the general type of arguments (assuming it is). However, you can use the value with std::common_type_t<T...> instead of a fixed type.
  • Is there a better way? There are several approaches, although I could imagine that the compiler really does a good job with this extension. An alternative that immediately comes to mind is return (args * ... * 1); , which, however, requires C ++ 17. If there is at least one argument, * 1 can be omitted: it should avoid a compile-time error if there is an empty list of parameter variables.
+4
source share

The code

 template<typename... T> u64 Multiply(T... args) { u64 res = 1; for (const u32& size : {args...}) res *= size; return res; } 

a little cryptic for me :-) Why do we have template parameters with type T and inside the method we used fixed-size values? And the variable name size looks very obscure, because this var has nothing to do with any size. And using integer types inside is also not a valid assumption if you are giving floating point data to a template.

OK, but to answer your question:

The first can be used with all types that you insert in a template function. The second used fixed (unsigned integer) types, which I do not expect if I see the declaration of the template itself.

Both versions can be made constexpr , as I found out now :-) and work very well for calculating compilation time.

To answer the question from your comment:

 {args...} 

expands to:

 { 1,2,3,4} 

which is just an "array" (std :: std :: initializer_list) and only works if all elements are of the same type.

So having

 for (const u32& size : {args...}) 

just iterates over the array.

+1
source share

All Articles