Is there a way in C++ to create a templated class that takes any number of arguments in the constructor and can get those when needed?
Example:
#include <string>
template<size_t Size, typename... Types>
class Container
{
public:
Container(Types... args)
{
// add checks to see if Size = no args
}
void get(Types&... args)
{
// getter method
}
};
int main()
{
Container<3, int, double, std::string> container(10, 2.5, "smth");
int a{};
double b{};
std::string c {};
container.get(a, b, c);
// expect a = 10, b = 2.5, c = "smth"
return 0;
}
Yes, a solution to your problem already exists in the form of std::tuple
:
// note: size_t parameter isn't needed, we can simply get the size using
// sizeof...(Types)
template<typename... Types>
class Container {
public:
std::tuple<Types...> stuff;
// note: in practice, this should use forwarding references and
// std::forward instead of taking everything by value
Container(Types... args) : stuff(args...) {}
void get(Types&... args) {
// std::tie creates a tuple of references, letting us
// assign all members at once
std::tie(args...) = stuff;
}
};
You can implement a container that wraps std::tuple
, but it doesn't really provide any utility that std::tuple
doesn't yet, so we can use it directly:
int main() {
std::tuple<int, double, std::string> container(10, 2.5, "smth");
// not so good: decompose using three variables and std::tie
int a {};
double b {};
std::string c {};
std::tie(a, b, c) = container;
// better: use structured bindings (C++17)
auto [aa, bb, cc] = container;
}
Keep in mind that outside of generic programming, you're almost always better off creating your own struct
, with meaningful names for the type and the members.
// aggregate type
struct Container {
int a;
double b;
std::string c;
};
int main() {
Container container{10, 2.5, "smth"};
// structured bindings also work for aggregate types (C++17)
auto [a, b, c] = container;
}