Andrey Rubliov
Andrey Rubliov

Reputation: 1595

Can folding expressions for variadic templates be binned, and conversion of argument list to vector used instead?

When folding expressions were introduced in C++ 17 I learnt how to write code for variadic templates using the form:

args OP ...

etc.

Now, generally in programming, when we have a long list of parameters, we use arrays or vectors, on which we can iterate with for or while loop.

I have just learnt that C++ allows to convert the packed argument list of a variadic template to simple vector like this:

template<class ... Args>
void foo()
{
   using arg_type = typename std::common_type_t<Args...>;

   std::vector<arg_type> arr = {args...};
}

Which make me conclude that now, for any variadic template instead of writing a strange code with folding expressions, I can freely iterate on the argument list using vector container.

Also, AFAIU, folding expressions technique allows to call only one binary operator on an argument and remaining folding expression, which is very limiting.

I mean, now I am absolutely free to do whatever I want with the arguments and not only to call some binary operator on them?

For example, I can print my arguments sorted like this:

    template<class ... Args>
    void printSorted(const Args& ... args)
    {
        using arg_type = typename std::common_type_t<Args...>;
        
        vector<arg_type> arg_vec = { args ... };
        
        sort(arg_vec.begin(), arg_vec.end());
        
        for(const auto& a : arg_vec)
        {
            cout << a << " ";
        }
        cout << endl;
    }

printSorted(3, 1, 5, 3, 7, 8, 2, 0);

Which I doubt I could do with folding expressions (and even if I could it would look terrible).

Now, for known template type parameters it is even simpler:

template<bool ... args>
int read_binary_value()
{
    const vector<bool> vals = { args ... };
    
    int res = 0;
    
    for(int i = 0; i < vals.size(); ++i)
    {
        res <<= 1;
        res += vals[i];
    }
    
    return res;
}

cout << read_binary_value<1, 0, 1>() << endl;

So am I right that folding expressions technique can now be binned and replaced by much more intuitive and readable technique of iterating over an array of arguments?

Upvotes: 0

Views: 147

Answers (1)

Jan Schultke
Jan Schultke

Reputation: 39658

So am I right that folding expressions technique can now be binned and replaced by much more intuitive and readable technique of iterating over an array of arguments?

No; all use cases you've shown work with "homogeneous packs" only. These are packs where all arguments have the same type, or are convertible to the same type.

using arg_type = typename std::common_type_t<Args...>;
std::vector<arg_type> arr = {args...};

This is obviously going to fail if there isn't a common type that all arguments are convertible to. For example, if Args is int* and float*.

Fold expressions are much more powerful because they work even when there is no common type, such as in:

#include <iostream>

void print(const auto&... args) {
    (std::cout << ... << args);
}

int main() {
    print(1, 3.5, "awoo"); // prints 13.5awoo
}

args can be a string, integer, pointer, etc., all in one pack.

If you have a homogeneous pack, you typically don't need variadic templates at all and can simply use std::array or std::initializer_list.

Also note that there is always a cost to wrapping a pack up in a container first. std::vector may have to dynamically allocate memory, which can be quite expensive.

Upvotes: 2

Related Questions