I cannot make my head around variadic tempates. I want to do very simple thing
Tuple t{1,2,3};
should crate tuple of size 3 containing array {1,2,3} ( t.data = {1,2,3})
Not sure but, if I understand correctly, your trying to re-create std::array.
What you want it's impossible before C++17 because your Tuple it's a template class, so before C++17 you needs to explicit the template arguments.
Starting from C++17, you can use deduction guides.
What you want (again: if I understand correctly) is almost the std::array deduction guide
template <class T, class... U>
array(T, U...) -> array<T, 1 + sizeof...(U)>;
In you case become
#include <type_traits>
template <typename T, std::size_t N>
struct Tuple
{
T data[N];
};
template <typename T, typename ... U>
Tuple(T, U...) -> Tuple<T, 1 + sizeof...(U)>;
int main ()
{
Tuple t{1, 2, 3};
static_assert( std::is_same_v<decltype(t), Tuple<int, 3u>> );
}
Observe that a constructor isn't strictly required because the argument are used to initialize the member (the C-style array).
This deduction guide
template <typename T, typename ... U>
Tuple(T, U...) -> Tuple<T, 1 + sizeof...(U)>;
deduce the type of the Tuple::data array from the first argument and the other argument are used only to deduce the size of the array; this can be a problem if the types of the arguments are different; by example
Tuple t1{1l, 2, 3}; // become Tuple<long, 3u>
Tuple t2{2, 2l, 3}; // become Tuple<int, 3u>
Take also in count that, for std::array
The program is ill-formed if (std::is_same_v<T, U> && ...) is not true
To solve this problem and have something more flexible, you can use std::common_type_t, as suggested in other answers, so deduction guide become
template <typename ... Ts>
Tuple(Ts...) -> Tuple<std::common_type_t<Ts...>, sizeof...(Ts)>;
and both cases become Tuple<long, 3u>
Tuple t1{1l, 2, 3}; // become again Tuple<long, 3u>
Tuple t2{2, 2l, 3}; // now become Tuple<long, 3u>
Perhaps I don't get difference between T...args and typename ...Args and args...
Look for a good C++ book but, making it simple
(1) typename ... Args declare a template variadic sequence of types, for a class/struct, for a using declaration, for a deduction guide, for a function.
So
template <typename ... Args>
struct foo
{ };
define a template struct that receive zero or more template types arguments and you can declare a variable as follows
foo<short, int, long, long long> f;
(2) T ... args declare a variadic template list not of types but of elements of type T
What is T? Another template parameter.
So, by example, one of your Tuple version in your question
template
struct Tuple
{ /* ... */ };
and a variable should be declared as follows
Tuple<int, 1, 2, 3> t{1, 2, 3}
that is very redundant in your case.
(3) args... (with ellipsis after the name) is the use a variadic list (of types or values)
By example
template <typename ... Args>
void foo (Args ... args)
{ bar(args...); }
declare and define a variadic template foo() function with a template variadic list of types
template <typename ... Args> // <--- declare a variadic list of types Args
and at every type correspond a value, so you declare also a variadic list args of values
void foo (Args ... args) // <--- declare a variadic list of args values of types Args
and the statement expand the pack of values args and pass they to another function
bar(args...); // <--- expand the args pack and pass the value to bar.