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

时间:2019-08-17 22:13:26

标签: c++ variadic-templates initialization-list

我无法绕着可变参数的诱惑。我想做一件很简单的事情

Tuple t{1,2,3};

应该创建包含数组{1,2,3}(t.data = {1,2,3})的大小为3的元组

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

  • 创建Tuple<T,3>大小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

3 个答案:

答案 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};

Demo