2016-12-02 178 views
1

假設我在編譯時有一個已知大小的std::vector,我想將它變成std::array。我會怎麼做?有沒有一個標準的功能來做到這一點?std :: vector用於std :: array初始化

我迄今爲止最好的解決辦法是這樣的:

template<class T, std::size_t N, class Indexable, std::size_t... Indices> 
std::array<T, N> to_array_1(const Indexable& indexable, std::index_sequence<Indices...>) { 
    return {{ indexable[Indices]... }}; 
} 

template<class T, std::size_t N, class Indexable> 
std::array<T, N> to_array(const Indexable& indexable) { 
    return to_array_1<T, N>(indexable, std::make_index_sequence<N>()); 
} 

std::array<Foo, 123> initFoos { 
    std::vector<Foo> lst; 
    for (unsigned i = 0; i < 123; ++i) 
    lst.push_back(getNextFoo(lst)); 
    return to_array<Foo, 123>(lst); // passing lst.begin() works, too 
} 

應用類似於Populate std::array with non-default-constructible type (no variadic templates):我也有一個類型,是不是默認 - 構造的,所以我需要計算的實際值數組被初始化。然而,與這個問題相反,對我來說,價值觀不僅僅是指數的函數,而且也是前面的值。我可以使用循環比一系列函數調用更容易地構建我的值。所以我構建了一個循環中的元素並將它們放入一個向量中,然後我想使用該向量的最終狀態來初始化該數組。

上述似乎編譯和工作正常,但也許有辦法來改善它。

  1. 也許我可以巧妙地使用一些我不知道的標準庫功能。
  2. 也許我可以以某種方式避免助手功能。
  3. 也許我可以用某種方式來表達它,以便它可以與元素的移動語義而不是上面使用的複製語義一起工作。
  4. 或許我能避免使用operator[]隨機訪問,而是使用前向迭代語義只所以會爲std::setstd::forward_list作爲輸入工作,太。
  5. 也許我應該停止使用std::vector,而是用std::array<std::optional<T>, N>代替使用C++ 17或某些等效實現來表達我的目標。

相關問題:

+1

有趣的問題,但這味道不好。爲什麼只想生成數據然後複製它們?這是你希望將它們打包在'std :: array'中的強制條件,但與原始的'std :: vector'相比,它的優點是什麼?兩者都將數據保存在連續的內存中,因此前者不應該佔用後者的優勢。唯一的區別是'size'是一個編譯時變量,但無論如何也是如此。 – Walter

+0

@Walter:有效的問題。一方面,我的原始是'std :: set',所以我至少需要轉換一次。而且我正在使用轉換構造函數來爲元素執行類型轉換。所以這不是隻保留'std :: vector'的問題。不過,我可以使用一個。主要反對意見可能是我的感覺,越是編譯器知道,它可以優化得越好。我在練習中只處理了24個元素,雖然這對於完整的循環展開來說可能太多了,但它可以例如知道計數可以被4整除,只是一種感覺。 – MvG

回答

3

我建議:

template<typename T, typename Iter, std::size_t... Indices> 
std::array<T, sizeof...(Indices)> to_array(Iter iter, std::index_sequence<Indices...>) { 
    return {{((void)Indices, *iter++)...}}; 
} 

template<typename T, std::size_t N, typename Iter> 
std::array<T, N> to_array(Iter iter) { 
    return to_array<T>(std::move(iter), std::make_index_sequence<N>{}); 
} 

這使得複製VS-移動語義到調用者–如果主叫方想移動時,可以選擇加入通過std::move_iterator或類似:

auto initFoos() { 
    std::vector<Foo> lst; 
    for (unsigned i{}; i != 123u; ++i) { 
     lst.push_back(getNextFoo(lst)); 
    } 
    // copy-init array elements: 
    return to_array<Foo, 123>(lst.cbegin()); 
    // or move-init array elements: 
    return to_array<Foo, 123>(std::make_move_iterator(lst.begin())); 
} 

Online Demo

+0

那裏有多個'iter ++'表達式的評估順序是什麼? –

+2

@MaximEgorushkin:一個brace-init-list內的所有擴展保證從左到右執行。 – ildjarn

+1

謝謝!這解決了我列表中的第3點和第4點,並且我還沒有意識到移動迭代器。在我的實際應用程序中,我使用了一個顯式的轉換構造函數,使用'Bar :: Bar(const Foo&)'和'Bar :: Bar(Foo &&)'從'vector '初始化'array '。事實證明,您提供的代碼並不算作明確的轉換,而我原來的代碼卻做到了。所以我在那裏添加了一個'T(...)'構造函數調用。 – MvG

相關問題