Initializer list for an unknown ("templated") amount of classes - c++

If I have a class template which contains an array with another class as type with undefined amount of fields (the amount is a template parameter), how do I run their constructors (if they take parameters)?
Here some example code:
class ArrayClass
{
public:
ArrayClass() = delete;
ArrayClass(int anyParameter) {}
};
template <const int amountOfFields>
class ContainingClass
{
ArrayClass myArray[amountOfFields];
public:
ContainingClass();
};
template <const int amountOfFields>
ContainingClass<amountOfFields>::ContainingClass()
:
myArray(5) // doesn't work of cause
{}
Is it possible to give every ArrayClass, no matter how many there are, the same parameter (or different ones)? (I don't essentially need it but it would make things easier for me)

There’s nothing in the C++ standard libraries for this case
If you’re compiling with GCC, it has a proprietary extension called ranged initialization. With GCC, you can write something like this (untested):
template<size_t amountOfFields>
ContainingClass<amountOfFields>::ContainingClass():
myArray( { [0 ... (amountOfFields-1)] = 5} )
{ }
If you’re using any other compiler, you have following options.
As said by the commenters, replace array with std::vector, it has the constructor you need. However this will change RAM layout, i.e. if you have lots of containers with small number of elements each, arrays (both C arrays, and C++ std::array) will be faster because one less pointer to chase.
Remove “=delete” from the default constructor of your ArrayClass, use std::fill or std::fill_n in the ContainingClass constructor to set initial values after they’re already constructed. However this might bring some small runtime cost.
If you don’t have too many elements, technically you can use some template metaprogramming to implement statically-constructed arrays the way you want. However, IMO that’ll be substantial amount of very hard to debug C++ code (there’s no compile-time debugger).
If you have small number of different template arguments in your code, you can write a function like
template<size_t N>
constexpr std::array<ArrayClass,N> fill_array(int val)
specialize it for different values of amountOfFields temple arguments you have, and call the function in the constructor of ContainingClass.
Other solutions are possible, like external tools, macros, boost, etc… But I think 2 & 4 are the most reasonable workarounds.

This work for me with GCC 8.1 / Clang 6.0 and C++14, though I am definitely not sure whether it is Standard compliant:
class E {
public:
E() = delete;
E(int i) : i_(i) { }
operator int() const { return i_; }
private:
int i_;
};
template <typename T>
T dummy(T val, /* [[maybe_unused]] */ size_t I) { return val; }
template <typename T, size_t... I, typename U>
constexpr auto make_array_impl(U val, std::index_sequence<I...> is) {
return std::array<T, is.size()>{dummy(val, I)...};
}
template <typename T, size_t N, typename U>
constexpr auto make_array(U val) {
return make_array_impl<T>(val, std::make_index_sequence<N>{});
}
template <typename T, size_t N>
class A {
public:
A(T val) : a_{make_array<T, N>(val)} { }
void print() { for (auto e : a_) std::cout << e << std::endl; }
private:
std::array<T, N> a_;
};
int main() {
A<E, 5> a(-1);
a.print();
}
Live demo: https://wandbox.org/permlink/Db9Zpf6gUMvg4MER
Updated more generic solution:
template <typename T, size_t... I, typename... Args>
constexpr auto make_array_impl(std::index_sequence<I...> is, Args&&... args) {
return std::array<T, is.size()>{ (I, T(std::forward<Args>(args)...))... };
}
template <typename T, size_t N, typename... Args>
constexpr auto make_array(Args&&... args) {
return make_array_impl<T>(std::make_index_sequence<N>{}, std::forward<Args>(args)...);
}

Related

How to write a size() function that works on any type of collection objects?

I require a simple way to obtain the count / length / size of an object of class T where T is some sort of collection type, such as a std::map, std::list, std::vector, CStringArray, CString, std::string, …
For most of the standard types, T::size() is the correct answer, for most of the MFC classes T::GetSize() is correct and for CString, it is T::GetLength().
I want to have a like:
template <typename T> auto size(const T & t)
...which evaluates to the correct member function call.
It seems like there should be a simple way to invoke a traits template on T which has a size(const T & t) member, which itself uses SFINAE to exist or not exist, and if it exists, then it is by definition calling an appropriate t.size_function() to return the count of elements in that instance of a T.
I could write an elaborate has_member type-trait template - there are a few examples on stackoverflow - all of them quite convoluted for what seems to me "there must be a simpler approach". With C++ 17, it seems like this issue should be easily and elegantly solved?
These discussions here and here seems to use an inelegant solution with some of the answers using preprocessor macros to get the job done. Is that still necessary?
But... surely, there must be a way to use the fact that calling the correct member function on a T is compilable, and calling the wrong one fails to compile - can't that be used directly to create the correct type traits wrapper for a given type T?
I would like something along the lines of:
template <typename T>
auto size(const T & collection)
{
return collection_traits<T>::count(collection);
}
Where the exact specialization of collection_traits<T> is selected because it is the only one that fits for T (i.e. it calls the correct instance method).
You can use expression SFINAE and multiple overloads.
The idea is as follows: check if x.size() is a valid expression for your type - if it is, invoke and return it. Repeat for .getSize and .getLength.
Given:
struct A { int size() const { return 42; } };
struct B { int getSize() const { return 42; } };
struct C { int GetLength() const { return 42; } };
You can provide:
template <typename T>
auto size(const T& x) -> decltype(x.size()) { return x.size(); }
template <typename T>
auto size(const T& x) -> decltype(x.getSize()) { return x.getSize(); }
template <typename T>
auto size(const T& x) -> decltype(x.GetLength()) { return x.GetLength(); }
Usage:
int main()
{
size(A{});
size(B{});
size(C{});
}
live example on wandbox.org
This solution is easy to extend and seamlessly works with containers that are templatized.
What if a type exposes two getters?
The solution above would result in ambiguity, but it's easy to fix by introducing a ranking/ordering that solves that.
Firstly, we can create a rank class that allows us to arbitrarily prioritize overloads:
template <int N> struct rank : rank<N - 1> { };
template <> struct rank<0> { };
rank<N> is implicitly convertible to rank<N - 1>. An exact match is better than a chain of conversions during overload resolution.
Then we can create a hierarchy of size_impl overloads:
template <typename T>
auto size_impl(const T& x, rank<2>)
-> decltype(x.size()) { return x.size(); }
template <typename T>
auto size_impl(const T& x, rank<1>)
-> decltype(x.getSize()) { return x.getSize(); }
template <typename T>
auto size_impl(const T& x, rank<0>)
-> decltype(x.GetLength()) { return x.GetLength(); }
Finally we provide an interface function that begins the dispatch to the right size_impl overload:
template <typename T>
auto size(const T& x) -> decltype(size_impl(x, rank<2>{}))
{
return size_impl(x, rank<2>{});
}
Using a type like D below
struct D
{
int size() const { return 42; }
int getSize() const { return 42; }
int GetLength() const { return 42; }
};
will now choose the rank<2> overload of size_impl:
live example on wandbox
The simplest solution, IMO, is function overloading.
// Default implementation for std containers.
template <typename Container>
std::size_t size(Container const& c) { return c.size(); }
// Overloads for others.
std::size_t size(CStringArray const& c) { return c.GetSize(); }
std::size_t size(CString const& c) { return c.GetLength(); }
// ... etc.
You need expression SFINAE, and you must play nice with other types which might decide to conform to both interfaces, so study std::size().
The goal is to augment std::size() to work on all types which follow at least one of the conventions, as long as they don't mess up trying to follow any of them.
#include <type_traits>
#include <iterator>
namespace internal {
// Avoid conflict with std::size()
template <class C>
auto size_impl(const C& c, int) -> decltype((void)c.size());
// Avoid conflict with std::size()
template <class T, std::size_t N>
void size_impl(const T (&array)[N], int);
template <class C>
constexpr auto size_impl(const C& c, long)
noexcept(noexcept(c.GetLength()))
-> decltype(c.GetLength())
{ return c.GetLength(); }
template <class C>
constexpr auto size_impl(const C& c, long long)
noexcept(noexcept(c.getSize()))
-> decltype(c.getSize())
{ return c.getSize(); }
};
template <class T>
using enable_if_not_void_t = std::enable_if_t<!std::is_void<T>(), T>;
using std::size;
template <class C>
constexpr auto size(const C& c)
noexcept(noexcept(internal::size_impl(c, 0)))
-> enable_if_not_void_t<decltype(internal::size_impl(c, 0))>
{ return internal::size_impl(c, 0); }
You can get arbitrary levels of precedence for extending things using templates and inheritance:
template <std::size_t N>
struct priority : priority<N - 1> {};
template <>
struct priority<0> {};
Something like the proposed Abbreviated Lambdas for Fun and Profit would greatly simplify things.

Generating a sequence of zeros at compile time

I have the following problem:
template< size_t... N_i >
class A
{
public:
// ...
void foo()
{
bar( /* 0,...,0 <- sizeof...(N_i) many */);
}
};
I want to call a function bar and pass sizeof...(N_i) many arguments to it which are all zeros, e.g., bar(0,0,0) in case sizeof...(N_i) == 3.
How can this be implemented?
bar(((void)N_i, 0)...);
The comma operator will discard N_i, yielding just the right-hand operand's value (0). The cast is to prevent a warning about N_i being discarded.
Despite the undoubtedly interesting answer by #Columbo, I want to suggest another viable solution based on constexpr'd template variables:
#include <cstddef>
template<std::size_t, std::size_t V>
constexpr std::size_t repeat_value = V;
template<std::size_t... N_i>
class A {
template<typename... Args>
void bar(Args&&...) { }
public:
void foo() {
// repeat N_i times value 0
bar(repeat_value<N_i, 0>...);
}
};
int main() {
A<0, 1, 2, 3, 4> a;
a.foo();
}
I find it easier to read at least, even if it's bad in terms of performance at compile-time.
You can easily generalize it as it follows:
template<std::size_t, typename T, T V>
constexpr T repeat_value = V;
The invokation in the specific case is this:
bar(repeat_value<N_i, int, 0>...);
You could also use templates to simulate something similar. This is a very basic solution and will just create a list of 0s, but it could be extended to generate other sequences if that was desired.
template <size_t Unused>
struct Wrap {
static constexpr size_t value = 0;
};
template <size_t... N_i>
class A {
public:
void foo() {
bar(Wrap<N_i>::value...);
}
};
Which will just expand into a list of zeros of the same size as the N_i arguments. Admittedly, the interface is slightly different.
For a complete example, which shows the values of the elements which bar receives, see here: Live example
You can also use the fact that if we subtract any positive integer from itself we get 0.
template< size_t... N_i >
class A
{
public:
void foo()
{
//------vvvvvvvvv-------->expand to N_i 0's
bar( (N_i - N_i)...);
}
//for ending recursion
void bar()
{
}
//generalized version
template<typename T, typename... Types>
void bar(T t, Types... types)
{
std::cout << t << "\n";
bar(types...);
}
};
int main()
{
A<1,2,3,4,10> a;
a.foo();
}

Place templated std::array<std::vector<T>, N> in constructor initializer list

I have a templated class containing a std::array<std::vector<T>, N> data_ member.
Currently, I can construct this with
template<typename T, size_t N>
class A
{
public:
A(some parameters)
{
for (size_t n=0; n<N; n++) {
data_[n].resize(calculated size from parameters);
}
}
private:
std::array<std::vector<T>,N> data_;
};
Is there some formulation in which I can instead place the initialization of the array in the constructor?
For example, if I used only a vector and then an indexing function, I could have
...
A(some parameters):
data_(std::vector<T>(N*previous size)) {}
...
#include <array>
#include <vector>
#include <utility>
template <typename T, std::size_t N>
class A
{
public:
A(int a, int b) : A(a, b, std::make_index_sequence<N>{})
{ }
private:
template <std::size_t... Is>
A(int a, int b, std::index_sequence<Is...>)
: data_{ { std::vector<T>(((void)Is, a + b))... } }
{ }
std::array<std::vector<T>, N> data_;
};
DEMO
Since the primary container that you initialise is std::array, and since it only provides aggregate initialisation and you want to parametrise on the size of the array, I'm afraid that you can't do that the way you want. you need to generate an initialisation sequence. This is as far as I know only possible with variadic templates. I tried to put something together, but using the approach from the other answer is clearly the best choice here.
On the other hand there's always the possibility to create a static member function that returns the array as you need it:
Constructor (bool data) : member(initialiser(data)) {}
static array<vector<T>, N> initialiser(bool data) {
array<vector<T>, N> container;
// your code from above
return container;
}
Though it's debatable and dependent on the context whether this is useful or not.

Is it possible to iterate over all elements in a struct or class?

Is it possible to iterate over all elements in a struct or class?
For example if I have a struct of three elements of different type:
struct A {
classA a;
classB b;
classC c;
};
then I need some iterator such that a method next() would give me the value
of the next element. The problem is that as you see, the values have different types.
Nope, not with the language as it is.
You could do it by deriving your classes from a common base, and then implementing your own iterator to return pointers to each item as the iterator is traversed.
Alternatively put the items in a std::vector and use that to provide the iteration.
No, there is no reflection in C++, (yet, there are murmurs about static reflection coming one day).
Anyway, there is a way to work around this, to an extent - first of all, you'll need a (temporary) tuple with references to your data members.
Then you will need a construct "iterating" over the tuple, such as:
void applyToAll() { }
template <typename Lambda, typename... Lambdas>
void applyToAll(Lambda&& closure, Lambdas&&... closures) {
std::forward<Lambda>(closure)();
applyToAll(std::forward<Lambdas>(closures)...);
}
// use your favourite sequence-making trick
template <unsigned... Is>
struct _Sequence {
typedef _Sequence<Is...> type;
};
template <unsigned Max, unsigned... Is>
struct _MakeSequence : _MakeSequence<Max - 1, Max - 1, Is...> { };
template <unsigned... Is>
struct _MakeSequence<0, Is...> : _Sequence<Is...> { };
template <typename Tuple, typename Functor, unsigned... Is>
void _foreachElemInTuple(_Sequence<Is...>, Tuple&& t, Functor&& f) {
applyToAll(
[&]{ std::forward<Functor>(f)(std::get<Is>(std::forward<Tuple>(t))); }...
);
}
template <typename Tuple, typename Functor>
void foreachElemInTuple(Tuple&& t, Functor&& f) {
_foreachElemInTuple(
_MakeSequence<std::tuple_size<
typename std::decay<Tuple>::type>::value>(),
std::forward<Tuple>(t), std::forward<Functor>(f)
);
}
Then you can call foreachElemInTuple(yourTuple, some_adapter()).
Your adapter will look like:
struct some_adapter {
template <typename... Args>
// A little bit of C++14, you can also just -> decltype the thing
decltype(auto) operator()(Args&& ... args) const {
return doStuff(std::forward<Args>(args)...);
}
};
As everyone else says, you cannot directly iterate over data members of a
class. However, it is not difficult to do it indirectly, provided of course that
you can access each of the data members you want to iterate over. The idea
in essense, as per ScarletAmaranth's solution, is to iterate over an std::tuple
of references to those data members.
The following program shows how to obtain such a tuple, using std::forward_as_tuple,
and another way to do the iterating by compiletime recursion, without
auxiliary apparatus.
#include <tuple>
/* You want to be able do something with the values of the members of an `A`
in turn.
*/
struct A
{
char ch;
int i;
double d;
// May also have members of class type. It doesn't matter
};
/* 1) Provide yourself with the means of creating a sequence that contains
references to the data members of a given `A`
*/
std::tuple<char const &, int const &, double const &> get_A_vals(A const & a)
{
return std::forward_as_tuple(a.ch,a.i,a.d);
}
/* 2) Provide yourself with a means of applying some operation, `Func`,
to each element of an `std::tuple`
*/
template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I == sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> const &, Func) {}
template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I < sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> const & tpl, Func func)
{
func(std::get<I>(tpl));
for_each_in_tuple<I + 1>(tpl,func);
}
/* 3) Combine 1) and 2) to apply `Func` over the members of an `A`
*/
template<typename Func>
void for_each_in_A(A const & a, Func func)
{
for_each_in_tuple(get_A_vals(a),func);
}
// Testing...
#include <iostream>
// A specimen operation: just prints its argument
struct printer
{
template<typename T>
void operator () (T && t)
{
std::cout << t << std::endl;
}
};
int main()
{
A a{'a',1,2.0};
for_each_in_A(a,printer());
return 0;
}
// EOF
The program outputs:
a
1
2
If you have control of the structs or classes over whose members you need to
iterate, you may consider whether it is practical simply to dispense with them
and use the corresponding std::tuples everywhere.
Code built with gcc 4.8.2 and clang 3.3, -std=c++11.

std::tuple and standard layout

If all of the members of std::tuple are of standard layout types, is that std::tuple itself standard layout? The presence of a user-defined copy-constructor makes it non-trivial, but I was wondering if it can still be standard layout.
A quote from the spec would be good.
No, standard layout requires that all nonstatic data members belong to either one base subobject or directly to the most derived type, and typical implementations of std::tuple implement one member per base class.
Because a member-declaration cannot be a pack expansion, in light of the above requirement, a standard layout tuple cannot have more than one member. An implementation could still sidestep the issue by storing all the tuple "members" inside one char[], and obtaining the object references by reinterpret_cast. A metaprogram would have to generate the class layout. Special member functions would have to be reimplemented. It would be quite a pain.
Inspired by PotatoSwatter's answer, I've dedicated my day to creating a standard layout tuple for C++14.
The code actually works, but is not currently suited for use as it involves undefined behaviour. Treat it as a proof-of-concept.
Here's the code I ended up with:
#include <iostream>
#include <type_traits>
#include <array>
#include <utility>
#include <tuple>
//get_size
template <typename T_head>
constexpr size_t get_size()
{
return sizeof(T_head);
}
template <typename T_head, typename T_second, typename... T_tail>
constexpr size_t get_size()
{
return get_size<T_head>() + get_size<T_second, T_tail...>();
}
//concat
template<size_t N1, size_t... I1, size_t N2, size_t... I2>
constexpr std::array<size_t, N1+N2> concat(const std::array<size_t, N1>& a1, const std::array<size_t, N2>& a2, std::index_sequence<I1...>, std::index_sequence<I2...>)
{
return { a1[I1]..., a2[I2]... };
}
template<size_t N1, size_t N2>
constexpr std::array<size_t, N1+N2> concat(const std::array<size_t, N1>& a1, const std::array<size_t, N2>& a2)
{
return concat(a1, a2, std::make_index_sequence<N1>{}, std::make_index_sequence<N2>{});
}
//make_index_array
template<size_t T_offset, typename T_head>
constexpr std::array<size_t, 1> make_index_array()
{
return {T_offset};
}
template<size_t T_offset, typename T_head, typename T_Second, typename... T_tail>
constexpr std::array<size_t, (sizeof...(T_tail) + 2)> make_index_array()
{
return concat(
make_index_array<T_offset, T_head>(),
make_index_array<T_offset + sizeof(T_head),T_Second, T_tail...>()
);
}
template<typename... T_args>
constexpr std::array<size_t, (sizeof...(T_args))> make_index_array()
{
return make_index_array<0, T_args...>();
}
template<int N, typename... Ts>
using T_param = typename std::tuple_element<N, std::tuple<Ts...>>::type;
template <typename... T_args>
struct standard_layout_tuple
{
static constexpr std::array<size_t, sizeof...(T_args)> index_array = make_index_array<T_args...>();
char storage[get_size<T_args...>()];
//Initialization
template<size_t T_index, typename T_val>
void initialize(T_val&& val)
{
void* place = &this->storage[index_array[T_index]];
new(place) T_val(std::forward<T_val>(val));
}
template<size_t T_index, typename T_val, typename T_val2, typename... T_vals_rest>
void initialize(T_val&& val, T_val2&& val2, T_vals_rest&&... vals_rest)
{
initialize<T_index, T_val>(std::forward<T_val>(val));
initialize<T_index+1, T_val2, T_vals_rest...>(std::forward<T_val2>(val2), std::forward<T_vals_rest>(vals_rest)...);
}
void initialize(T_args&&... args)
{
initialize<0, T_args...>(std::forward<T_args>(args)...);
}
standard_layout_tuple(T_args&&... args)
{
initialize(std::forward<T_args>(args)...);
}
//Destruction
template<size_t T_index, typename T_val>
void destroy()
{
T_val* place = reinterpret_cast<T_val*>(&this->storage[index_array[T_index]]);
place->~T_val();
}
template<size_t T_index, typename T_val, typename T_val2, typename... T_vals_rest>
void destroy()
{
destroy<T_index, T_val>();
destroy<T_index+1, T_val2, T_vals_rest...>();
}
void destroy()
{
destroy<0, T_args...>();
}
~standard_layout_tuple()
{
destroy();
}
template<size_t T_index>
void set(T_param<T_index, T_args...>&& data)
{
T_param<T_index, T_args...>* ptr = reinterpret_cast<T_param<T_index, T_args...>*>(&this->storage[index_array[T_index]]);
*ptr = std::forward<T_param<T_index, T_args...>>(data);
}
template<size_t T_index>
T_param<T_index, T_args...>& get()
{
return *reinterpret_cast<T_param<T_index, T_args...>*>(&this->storage[index_array[T_index]]);
}
};
int main() {
standard_layout_tuple<float, double, int, double> sltuple{5.5f, 3.4, 7, 1.22};
sltuple.set<2>(47);
std::cout << sltuple.get<0>() << std::endl;
std::cout << sltuple.get<1>() << std::endl;
std::cout << sltuple.get<2>() << std::endl;
std::cout << sltuple.get<3>() << std::endl;
std::cout << "is standard layout:" << std::endl;
std::cout << std::boolalpha << std::is_standard_layout<standard_layout_tuple<float, double, int, double>>::value << std::endl;
return 0;
}
Live example: https://ideone.com/4LEnSS
There's a few things I'm not happy with:
Alignment is not handled properly which means entering misaligned types will currently give you undefined behaviour
Not all tuple functionality is represented yet.
I don't think the memory management is currently exception-safe.
It uses tuple to determine the type for each index.
The overall code quality is kinda messy.
There might be better, more concise ways to handle some of the recursive template functions.
I don't fully understand everything I did. I understand the main basics, but I'm using some of the weirder syntax on good faith. Here's the most important sources I used:
Create N-element constexpr array in C++11
Lookup table with constexpr
c++11 constexpr flatten list of std::array into array
constexpr, arrays and initialization
template parameter packs access Nth type and Nth element
This is not yet suitable for use as-is, really only treat it as a proof-of-concept in this state. I will probably come back to improve on some of these issues. Or, if anyone else can improve it, feel free to edit.
One reason std::tuple cannot be of standard layout, as any classes with members and base classes with members, is that the standard allows for space optimization when deriving even non-empty base classes. For example:
#include <cstdio>
#include <cstdint>
class X
{
uint64_t a;
uint32_t b;
};
class Y
{
uint16_t c;
};
class XY : public X, public Y
{
uint16_t d;
};
int main() {
printf("sizeof(X) is %zu\n", sizeof(X));
printf("sizeof(Y) is %zu\n", sizeof(Y));
printf("sizeof(XY) is %zu\n", sizeof(XY));
}
Outputs:
sizeof(X) is 16
sizeof(Y) is 2
sizeof(XY) is 16
The above shows that the standard allows for class trailing padding to be used for the derived class members. Class XY has two extra uint16_t members, yet its size equals to the size of base class X.
In other words, class XY layout is the same as that of another class that has no base classes and all the members of XY ordered by address, e.g. struct XY2 { uint64_t a; uint32_t b; uint16_t c; uint16_t d; };.
What makes it non-standard layout is that the size of a derived class is not a function of sizes of base classes and derived class members.
Note that the size of a struct/class is a multiple of the alignment of one of its members with the largest alignment requirement. So that an array of objects is suitably aligned for such a member. For built-in types normally sizeof(T) == alignof(T). Hence sizeof(X) is a multiple of sizeof(uint64_t).
I am not sure whether the standard requires special treatment for struct, but with g++-5.1.1 if class is replaced with struct the above code yields different output:
sizeof(X) is 16
sizeof(Y) is 2
sizeof(XY) is 24
In other words, the trailing padding space optimization is not used when struct is involved (did not test for exact conditions).
The "list" approach can be used to get standard layout tuple (the following example has some inaccuracies but demonstrates the idea):
template <class... Rest>
struct tuple;
template <class T, class... Rest>
struct tuple<T, Rest...> {
T value;
tuple<Rest...> next;
};
template <>
struct tuple<> {};
namespace details {
template <size_t N>
struct get_impl {
template <class... Args>
constexpr static auto process(const tuple<Args...>& t) {
return get_impl<N - 1>::process(t.next);
}
};
template <>
struct get_impl<0> {
template <class... Args>
constexpr static auto process(const tuple<Args...>& t) {
return t.value;
}
};
}
template <size_t N, class... Args>
constexpr auto get(const tuple<Args...>& t) {
return details::get_impl<N>::process(t);
}
template <class... Args>
constexpr auto make_tuple(Args&&... args) {
return tuple<Args...>{std::forward<Args>(args)...};
}