Lee
Lee

Reputation: 23

Using Variadic Function to pass args into a vector

I am trying to make a function that adds an unknown amount of objects to a vector. I am trying to accomplish it here by just passing ints, but I cannot get it to work. Does any one know how this can be done?

Code

#include <iostream>
#include <vector>

class Entity
{
public:
    std::vector<int> Ints;
    
    template <typename T, typename ... pack>
    void AddToVector(T first, pack ... argPack)
    {
        Ints.push_back(argPack...);
        
        for(auto& i : Ints)
            std::cout << i << "\n" << std::endl;
    };
};


int main()
{
    Entity e1;
    
    e1.AddToVector(1, 2, 3, 4, 5);
    return 0;
}

Error:

main.cpp: In instantiation of ‘void Entity::AddToVector(T, pack ...) [with T = int; pack = {int, int, int, int}]’:
main.cpp:32:33</span>:   required from here
main.cpp:20:9: error: no matching function for call to ‘std::vector::push_back(int&, int&, int&, int&)’

Upvotes: 1

Views: 164

Answers (5)

Elliott
Elliott

Reputation: 2623

From c++17 you can use a very simple fold expression for this:

template <class ... Ts>
void AddToVector(Ts ... args)
{
    (Ints.push_back(args), ...);
}

demo

Upvotes: 0

Remy Lebeau
Remy Lebeau

Reputation: 595897

In C++11 and C++14, You can use something like this:

private:
    void Internal_AddToVector()
    {
    }

    template <typename T, typename ... pack>
    void Internal_AddToVector(T first, pack... argPack)
    {
        Ints.push_back(first);
        Internal_AddToVector(argPack...);
    }

public:
    template <typename ... pack>
    void AddToVector(pack ... argPack)
    {
        Internal_AddToVector(argPack...);

        for(auto& i : Ints)
            std::cout << i << "\n" << std::endl;
    }

Alternatively:

public:
    template <typename ... pack>
    void AddToVector(pack ... argPack)
    {
        for (auto& elem : {argPack...})
            Ints.push_back(elem);

        for(auto& i : Ints)
            std::cout << i << "\n" << std::endl;
    }

In C++17 and later, you can use a fold expression instead:

public:
    template <typename ... pack>
    void AddToVector(pack... argPack)
    {
        (Ints.push_back(argPack), ...);

        for(auto& i : Ints)
            std::cout << i << "\n" << std::endl;
    }

Upvotes: 4

pigrammer
pigrammer

Reputation: 3489

You want to push back all the values. push_back only accepts one argument. If you're using C++17 or greater, use a fold expression:

class Entity
{
public:
    std::vector<int> Ints;
    
    template <typename T, typename ... pack>
    void AddToVector(T first, pack ... argPack)
    {
        (Ints.push_back(argPack), ...);
        
        for(auto& i : Ints)
            std::cout << i << "\n" << std::endl;
    };
};


int main()
{
    Entity e1;
    
    e1.AddToVector(1, 2, 3, 4, 5);
    return 0;
}

Otherwise, a for loop:

class Entity
{
public:
    std::vector<int> Ints;
    
    template <typename T, typename ... pack>
    void AddToVector(T first, pack ... argPack)
    {
        for (auto& elem : {argPack...}) Ints.push_back(elem);
        
        for(auto& i : Ints)
            std::cout << i << "\n" << std::endl;
    };
};


int main()
{
    Entity e1;
    
    e1.AddToVector(1, 2, 3, 4, 5);
    return 0;
}

Upvotes: 0

XPhyro
XPhyro

Reputation: 528

[[maybe_unused]] int dummy[sizeof...(pack)] = {(Ints.push_back(argPack), 0)...};

or

([&]{Ints.push_back(argPack);}(), ...);

will do the job. I'm not sure what standard these are supported onwards.

Upvotes: 0

Sam Varshavchik
Sam Varshavchik

Reputation: 118320

Ints.push_back(argPack...);

If, for example, there are for parameters this becomes:

Ints.push_back(1, 2, 3, 4);

If you refer to the specifications for push_back() you will find out that it takes exactly one parameter, and not four. Hence the compilation error.

The fact that your template is declares thusly:

    template <typename T, typename ... pack>
    void AddToVector(T first, pack ... argPack)

this strongly suggests that your assignment's intention is for the template function to push_back() one value at a time:

Ints.push_back(first);

And then recursively invoke itself:

AddToVector(argPack...);

That, of course, would not be very optimal, however I'm optimistic that modern C++ compilers will be able to optimize most of this away.

But there is still one more detail that must be taken care of: an empty parameter pack. This is a guaranteed eventuality and overload resolution will fail. Therefore you must provide a do-nothing overload, for that eventuality:

void AddToVector()
{
}

Upvotes: 2

Related Questions