我无法绕着可变参数的诱惑。我想做一件很简单的事情
Tuple t{1,2,3};
应该创建包含数组{1,2,3}(t.data = {1,2,3}
)的大小为3的元组
这意味着它应该做两件事:
Tuple<T,3>
大小3(Tuple<>::data[3]
)Tuple<>::data
这不起作用:
template<typename T, T...args>
struct Tuple{
T data[sizeof...(args)];
Tuple(const T& args...):data{args...}{};
};
我尝试了各种变体,例如:
template<typename T, T...args>
//template<typename T, Args...args>
struct Tuple{
T data[sizeof...(args)];
//T data{args...};
//template <typename ...Args>
//Tuple(T... args):data{args...}{};
Tuple(const T& args...):data{args...}{};
//Tuple(T* ...args):data{args...}{};
};
也许我之间没有区别
T...args
和typename ...Args
和args...
我正尝试将其作为简单的示例来理解可变参数模板,并避免使用std::initializer_list
答案 0 :(得分:1)
我无法绕着可变参数的诱惑。我想做一件很简单的事情
Tuple t{1,2,3};
应创建3个包含
array {1,2,3}
(t.data = {1,2,3}
)的元组
不确定,但是,如果我理解正确,则您尝试重新创建std::array
。
您想要的东西在C ++ 17之前是不可能的,因为Tuple
是模板类,因此在C ++ 17之前,您需要显式声明模板参数。
从C ++ 17开始,您可以使用演绎指南。
您想要的(再次:如果我理解正确的话)几乎是std::array
deduction guide
template <class T, class... U> array(T, U...) -> array<T, 1 + sizeof...(U)>;
如果您成为
#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>> );
}
请注意,由于该参数用于初始化成员(C样式数组),因此并非严格要求构造函数。
此推导指南
template <typename T, typename ... U>
Tuple(T, U...) -> Tuple<T, 1 + sizeof...(U)>;
从第一个参数和另一个参数推导Tuple::data
数组的类型仅用于推导数组的大小;如果参数的类型不同,则可能会出现问题;例如
Tuple t1{1l, 2, 3}; // become Tuple<long, 3u>
Tuple t2{2, 2l, 3}; // become Tuple<int, 3u>
还算上std::array
如果
(std::is_same_v<T, U> && ...)
不是true
,则程序格式错误
要解决此问题并提供更灵活的方法,可以使用std::common_type_t
,如其他答案中所建议的那样,以便推论指南成为
template <typename ... Ts>
Tuple(Ts...) -> Tuple<std::common_type_t<Ts...>, sizeof...(Ts)>;
,两种情况都变成Tuple<long, 3u>
Tuple t1{1l, 2, 3}; // become again Tuple<long, 3u>
Tuple t2{2, 2l, 3}; // now become Tuple<long, 3u>
也许我在T ... args和类型名... Args和args之间没有区别...
寻找一本好的C ++书籍,但是要使其简单
(1)typename ... Args
为类/结构,为using
声明,为演绎指南,为函数声明类型的模板可变参数序列。
所以
template <typename ... Args>
struct foo
{ };
定义一个接收零个或多个模板 types 自变量的模板结构,您可以按以下方式声明变量
foo<short, int, long, long long> f;
(2)T ... args
声明一个可变参数模板列表,该列表不是T
类型的元素,而是
什么是T
?另一个模板参数。
例如,您问题中的Tuple
版本之一
模板 结构元组 {/ * ... * /};
,并且变量应声明如下
Tuple<int, 1, 2, 3> t{1, 2, 3}
这对您来说是非常多余的。
(3)args...
(名称后带有省略号)是使用可变参数列表(类型或值)
通过示例
template <typename ... Args>
void foo (Args ... args)
{ bar(args...); }
使用模板可变参数类型列表声明和定义可变参数模板foo()
template <typename ... Args> // <--- declare a variadic list of types Args
,并且每种类型都对应一个值,因此您还要声明一个可变的值列表args
void foo (Args ... args) // <--- declare a variadic list of args values of types Args
和该语句扩展值args
的包并将其传递给另一个函数
bar(args...); // <--- expand the args pack and pass the value to bar.
答案 1 :(得分:0)
这是非常困难的。我想做这项工作的唯一方法是将数组大小作为模板参数,而不是以某种方式从实际的构造函数参数中推导出来,并使用C ++ 17推导指南。
在gcc 9.1和-std=c++17
上进行了测试:
#include <cstdlib>
#include <iostream>
#include <type_traits>
#include <utility>
template<typename T, size_t n>
struct Tuple{
T data[n];
template<typename ...Args>
Tuple(Args && ...args):data{std::forward<Args>(args)...}{};
};
template<typename ...Args>
Tuple(Args && ...args)
-> Tuple<std::common_type_t<std::remove_reference_t<Args>...>,
sizeof...(args)>;
Tuple a{1,2,3,4,5};
int main()
{
std::cout << std::is_same_v<decltype(a),
Tuple<int, 5>> << std::endl;
std::cout << a.data[2] << std::endl;
}
答案 2 :(得分:0)
使用std::index_sequence
的替代方法:
template <typename T, std::size_t> using always_t = T;
template <typename T, typename Seq> struct Tuple;
template <typename T, std::size_t...Is>
struct Tuple<T, std::index_sequence<Is...>>{
T data[sizeof...(Is)];
Tuple(const always_t<T, Is>&... args) : data{args...}{}
};
// Deduction guide (C++17)
template <typename ... Ts>
Tuple(const Ts&...) -> Tuple<std::common_type_t<Ts...>, std::index_sequence_for<Ts...>>;
Tuple a{1,2,3,4,5};