在可变参数模板类中初始化数组



我无法理解可变参数的诱惑。我想做非常简单的事情

Tuple t{1,2,3};

应装箱大小为 3 的包含数组 {1,2,3} 的元组 (t.data = {1,2,3}(

这意味着它应该做两件事:

  • 创建 3 号Tuple<T,3>(Tuple<>::data[3](
  • 用数字形式 std::initializer_list 填写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...argstypename ...Argsargs...

我试图将此用作简单示例来理解可变参数模板并避免使用std::initializer_list

我无法解决可变参数的诱惑。我想做非常简单的事情

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演绎指南

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

如果未true,则程序格式不正确(std::is_same_v<T, U> && ...)

要解决此问题并具有更灵活的东西,您可以使用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之间的区别...参数和类型名称...哗啦啦啦啦�

寻找一本好C++书,但要让它变得简单

(1(typename ... Args为类/结构、using声明、演绎指南、函数声明一个模板可变参数类型序列。

所以

template <typename ... Args>
struct foo
{ };

定义接收零个或多个模板类型参数的模板结构,您可以按如下方式声明变量

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.

使用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};

演示

这是非常困难的。我能想到的唯一方法是将数组大小作为模板参数,而不是以某种方式从实际的构造函数参数中推断出来,并使用 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;
}

基于这个很好的解释。

#1 创建第 n 个生成的整数数组

template <typename Container, int... I>
Container iota_impl(std::integer_sequence<int, I...>) {
return {I...};
}
template <typename T, size_t N>
auto iota_array() {
using Sequence = std::make_integer_sequence<int, N>;
return iota_impl<std::array<T, N>>(Sequence{});
}
...
auto arr1 = iota_array<int, 10>();

将创建std::array<int,>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

#2 创建一个通过initializer_list传递的整数数组

template <typename T, T...I>
auto iota_array2() {
constexpr auto N = sizeof...(I);
return std::array<T, N>({I...});
}
...
auto arr2 = iota_array2<int, 3,2,7,4,5,6>();

将创建std::array<int,>{3,2,7,4,5,6}

PS 如果它应该包装在元组中,它可以是.
PPS c++17

最新更新