簡體   English   中英

在可變參數模板類中初始化數組

[英]Initialize array inside variadic template class

我無法繞開可變的模板。 我想做很簡單的事

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

我無法繞開可變的模板。 我想做很簡單的事

Tuple t{1,2,3};

應該包含array {1,2,3} ( t.data = {1,2,3} ) 的大小為 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

如果(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 和 typename ...Args 和 args 之間的區別...

尋找一本好的 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, 10>{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, 6>{3,2,7,4,5,6}

PS如果應該用Tuple包裹,可以。
PPS c++17

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM