[英]Best strategy for initializing static arrays in a struct in C++
假設該結構提供了兩個模板 arguments,一個是typename T
,另一個是size_t N
現在,該結構應該存儲元素類型T
和大小N
的 static 數組。 在某些情況下,在創建實例時使用默認值初始化數組可能會很好,如果你有類似的東西,我認為無論如何都會發生這種情況
template<typename T, size_t N>
struct Foo {
T values[N];
size_t size;
explicit Foo(size_t _size) : size{ _size } {}
}
因此,如前所述,我認為這種情況下的行為是values
會自動使用默認值初始化(調用T
的默認構造函數)。 但是,如果我想在構造 object 時為數組傳遞一些值怎么辦? 這里的目標是我能夠將 static 數組傳遞給構造函數,並讓 static 數組代替values
。 理想情況下,整個過程中只有兩個數組創建。 但這可能嗎? 考慮以下示例:
template<typename T, size_t N>
struct Foo {
T values[N];
size_t size;
explicit Foo(T _values[N], size_t _size) : values{ _values }, size{ _size} {}
}
現在,除了我什至不知道上述是否會按預期工作之外,我仍然有點不確定 C++ 何時發生副本。 我想在最壞的情況下,調用Foo
的構造函數時會創建 4 個 arrays :
Foo
的構造函數,您需要傳遞一個數組,因此您需要事先創建它。_values
是按值傳遞的,所以這里發生了一個副本。_values
分配給values
時,正在發生另一個副本(?) 現在正如所說,我真的不確定 C++ 中的復制行為。 當然,我們可以通過使_values
參數按引用傳遞來排除創建一個數組的可能性。 但是,static 數組values
仍然會在被_values
覆蓋之前被初始化,或者我認為。
因此,我的問題是:這里最好的策略是什么? 我如何編寫代碼才能觸發最少的數組創建?
謝謝!
編輯:
不,我不能使用std::vector
或 stdlib 中的任何其他數據結構。 即使可以,我的問題仍然是關於原始 arrays的最佳策略,而不是如何切換我的方法以支持某些包裝器。
為了避免復制和指針衰減, _values
可以通過引用傳遞:
template<typename T, std::size_t N>
struct Foo {
T values[N];
std::size_t size;
explicit Foo(T const(&_values)[N], std::size_t _size) : size{ _size}{
std::copy(std::begin(_values), std::end(_values), std::begin(values));
}
};
https://godbolt.org/z/dnb9zTooG
但最好使用std::array<T, N> values;
成員。
這個答案引起了一些爭議。 需要明確的是,如果問題是嵌套數組的初始化,最好不要(任何)構造函數並使用聚合初始化。 如果您想用 class 做其他事情,這是非常有限的。 最后,無論如何您都在重新發明std::array
。
你可以這樣做:
#include <cstddef>
#include <type_traits>
#include <utility>
#include <algorithm>
using namespace std;
struct S
{
S();
S(int);
S(S const&);
S(S&&);
~S();
S& operator=(S const&);
S& operator=(S&&);
};
template<class F>
struct inplacer
{
F f_;
operator std::invoke_result_t<F&>() { return f_(); }
};
template<class F> inplacer(F) -> inplacer<F>;
template<size_t maxN, typename T>
struct myarray
{
size_t sz_{};
T data_[maxN];
template<class... Ts>
myarray(Ts&&... args) : sz_{sizeof...(Ts)}, data_{forward<Ts>(args)...} {}
template<class E, size_t N>
static myarray make(E const (&a)[N], size_t count = N)
{
return make_copy(a, count, make_index_sequence<maxN>());
}
template<class E, size_t N>
static myarray make(E (&&a)[N], size_t count = N)
{
return make_move(a, count, make_index_sequence<maxN>());
}
private:
template<class E, size_t... Indices>
static myarray make_copy(E const* p, size_t count, index_sequence<Indices...>)
{
auto r = myarray( inplacer{ [&]{ return Indices < count ? p[Indices] : T(); } }... );
r.sz_ = min(count, maxN);
return r;
}
template<class E, size_t... Indices>
static myarray make_move(E* p, size_t count, index_sequence<Indices...>)
{
auto r = myarray( inplacer{ [&]{ return Indices < count ? move(p[Indices]) : T(); } }... );
r.sz_ = min(count, maxN);
return r;
}
};
auto f()
{
auto v = myarray<16, int>(1, 2);
//myarray<100, int> v = {3, 4};
//static int d[] = {1, 2, 3, 4};
//auto v = myarray<12, int>::make(d, 3);
//myarray<4, S> v = {S(1), 2};
//myarray<4, S> v = {inplacer{ []{ return S(1); } }, 2};
//static S d[] = {3, 4, 1};
//auto v = myarray<4, S>::make(d, 2);
//auto v = myarray<4, S>::make(move(d));
return v;
}
我敢打賭,這可以在很多方面進行改進,我將把它留給你......
筆記:
無論我多么努力——我無法讓 GCC默認初始化未使用的數組元素。 我試圖用trick<T>()
調用的結果填充它們,但它沒有用......它抱怨未使用的變量並堅持用零填充數組的rest。
template<class T> T trick() { T r; return r; }
這意味着這個 class 效率很低,更好的方法是使用原始存儲並手動控制它(也許嘗試將數組放入無限制的聯合?但要注意——編譯器在處理聯合時有錯誤)
還要記住,編譯器不喜歡太多的 function 參數——相關的優化只適用於相對較小的maxN
值。 為myarray<1000, int>::make(d, 3)
生成的代碼看起來很可怕,編譯時間也很短......
myarray
ctor 允許類似聚合初始化的初始化,它非常接近但不完全相同。 它不允許就地構造(僅僅是因為 arguments 需要在調用之前構造)——您可以使用inplacer
來克服這個問題(參見f()
中的示例之一)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.