Reputation: 109
I am currently trying to implement a general initializer to reduce the size of our codebase. At one point however, my code looked like this:
template<typename T, typename Arg1, typename Arg2>
T* ManageDevice(Arg1 arg1, Arg2 arg2)
{
auto device = new T{ arg1, arg2 };
// More operations on device
return device;
}
template<typename T, typename Arg1, typename Arg2, typename Arg3>
T* ManageDevice(Arg1 arg1, Arg2 arg2, Arg3 arg3)
{
auto device = new T{ arg1, arg2, arg3 };
// More operations on device
return device;
}
template<typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
T* ManageDevice(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
{
auto device = new T{ arg1, arg2, arg3, arg4 };
// More operations on device
return device;
}
Which starts to be less elegant. From my understanding, variadic templates seems to be the way to solve this issue. But I don't understand how this can be applied to my situation.
I would prefer something like this:
T* ManageDevice(Args... args)
{
// The function I want
// Unpack as a std::initializer_list
auto allArguments = unpackAll();
auto device = new T{ allArguments };
// More operations on device
return device;
}
Any suggestion on how unpackAll() should be implemented? Thanks.
Upvotes: 5
Views: 5202
Reputation: 1526
My C++14 answer, as a minimal working example
#include <initializer_list>
#include <utility>
#include <vector>
#include <type_traits>
#include <iostream>
struct example {
template <typename ...Args, typename T = std::common_type_t<Args...>>
static std::vector<T> foo(Args&& ...args) {
std::initializer_list<T> li{std::forward<Args>(args)...};
std::vector<T> res{li};
return res;
}
};
int main() {
std::vector<int> v1 = example::foo(1,2,3,4);
for(const auto& elem: v1)
std::cout << elem << " ";
std::cout << "\n";
}
You need to edit this for your needs a bit, to your code structure that is. But note that vector
constructor takes initializer_list
and that list is generated from parameter pack in that static foo
method.
Edit: In your case, as others noted, you can just directly forward your parameter pack to your call. My answer shows passing them to an initializer_list
.
So in fact, you can just do
static std::vector<T> foo(Args&& ...args) {
std::vector<T> res{std::forward<Args>(args)...};
return res;
}
and those arguments will be implicitly converted to an initializer_list
. I explicitly showed the construction of an initializer_list
from a parameter pack.
Upvotes: 1
Reputation: 180500
auto device = new T{ allArguments };
just needs to be
auto device = new T{ args... };
In T{ args... }
args...
will expand the parameter pack to arg0, arg1, ..., argn
for you.
You can see this working with
template <typename... Args>
std::vector<int> make_vector(Args... args)
{
return {args...};
}
int main()
{
auto foo = make_vector(1,2,3,4);
for (auto e : foo)
std::cout << e << " ";
}
Edit to add perfect forwarding version
template <typename... Args>
std::vector<int> make_vector(Args&&... args)
{
return {std::forward<Args>(args)...};
}
Upvotes: 5