我正在用 C 撰寫一個多維陣列類,并希望擁有它的靜態和動態記憶體版本。根據我之前一篇博文中的回答,我使用 CRTP 定義了一個基類,然后該基類可以訪問派生類中具有連續記憶體的相應資料容器(std::array 和 std::vector)。
我希望在基類本身中實作多維下標運算子(例如,訪問 4x3 矩陣的 (1, 2) 條目,該矩陣的 12 個條目連續存盤在記憶體中)以避免代碼重復。請注意,我剛剛選擇了下標運算子,因為它是說明我希望實作的最簡單的類方法。我試圖將@Jarod42在這里給出的多維下標線性化解決方案應用于我的 CRTP 案例。但是,我不確定如何在派生類中存盤陣列維度,以便在計算線性化索引時在基類中推斷它們。我希望以下代碼清楚地說明了我的問題(我正在使用 C 20 進行編譯):
#include <array>
#include <cassert>
#include <vector>
/** Multi-dimensional Array Base Class with CRTP. */
template <class derived, class T>
class MultiDimArray
{
private:
constexpr derived& Derived() noexcept { return static_cast<derived&>(*this); }
constexpr const derived& Derived() const noexcept { return static_cast<const derived&>(*this); }
protected:
constexpr MultiDimArray() {}
public:
// I've butchered the syntax in the following subscript operators, but I hope it captures what I intend to do.
constexpr const T& operator()(HookTypeToPack<Derived().Dimensions, std::size_t> ...Is) const {
return *(this->Derived()[ComputeIndex<Derived().Dimensions...>(Is...)]);
}
constexpr T& operator()(HookTypeToPack<Derived().Dimensions, std::size_t> ...Is) {
return *(this->Derived()[ComputeIndex<Derived().Dimensions...>(Is...)]);
}
};
/** Static Multi-dimensional Array Class */
template <class T, std::size_t ...Dims>
class StaticMultiDimArray : public std::array<T, (Dims * ...)>,
public MultiDimArray<StaticMultiDimArray<T, (Dims * ...)>, T>
{
private:
constexpr static std::size_t nEntries = (Dims * ...);
constexpr static std::tuple<HookTypeToPack<Dims, std::size_t>...> Dimensions = std::make_tuple(Dims...);
friend MultiDimArray<StaticMultiDimArray<T, (Dims * ...)>, T>;
public:
constexpr StaticMultiDimArray() : std::array<T, nEntries>(InitStaticArray<T, nEntries>(0.0)) {}
};
/** Dynamic Multi-dimensional Array Class */
template <class T>
class DynamicMultiDimArray : public std::vector<T>, public MultiDimArray<DynamicMultiDimArray<T>, T>
{
private:
std::size_t nEntries;
std::tuple<...> Dimensions; // Not sure what to use here considering a tuple is immutable
friend MultiDimArray<DynamicMultiDimArray<T>, T>;
public:
DynamicMultiDimArray() : std::vector<T>() {}
template <typename ...Dims>
DynamicMultiDimArray(Dims ...dimensions) : std::vector<T>((dimensions * ...), 0.0) { Resize(dimensions...); }
template <typename ...Dims>
void Resize(Dims ...dimensions)
{
nEntries = (dimensions * ...);
this->resize(nEntries);
// store dimensions in Dimensions...
}
};
上面使用的支持函式InitStaticArray、HookTypeToPack和ComputeIndex定義如下:
template <class T, std::size_t size>
constexpr auto InitStaticArray(const T& value)
{
std::array<T, size> arr;
std::fill(arr.begin(), arr.end(), value);
return arr;
}
template <std::size_t, typename T>
using HookTypeToPack = T;
template <std::size_t ...Dims>
constexpr std::size_t ComputeIndex(HookTypeToPack<Dims, std::size_t> ...multi_index)
{
constexpr std::size_t dims_arr[] = {Dims...};
std::size_t multi_index_arr[] = {multi_index...};
std::size_t index(0), factor(1);
for(int i = 0; i < sizeof...(Dims); i )
{
assert(0 <= multi_index_arr[i] && multi_index_arr[i] < dims_arr[i]);
index = factor * multi_index_arr[i];
factor *= dims_arr[i];
}
assert(0 <= index && index < (Dims * ...));
return index;
}
任何人都可以給我一些關于如何存盤陣列維度的建議StaticMultiDimArray,DynamicMultiDimArray以便可以在 中的operator()多載中適當地呼叫它們MultiDimArray嗎?
uj5u.com熱心網友回復:
有幾種選擇。您已經暗示過:將維度存盤在派生類中。但是,不要使用std::tuple; 這允許每個元素具有不同的型別,而我們只希望每個維度的大小為std::size_t. std::array對靜態陣列使用 a ,std::vector對于動態陣列:
template <class T, std::size_t ...Dims>
class StaticMultiDimArray :
public std::array<T, ...>,
public MultiDimArray<...>
{
constexpr static std::array dimensions{Dims...};
...
};
template <class T>
class DynamicMultiDimArray :
public std::vector<T>,
public MultiDimArray<...>
{
std::vector<std::size_t> dimensions;
...
};
您也不需要創建DynamicMultiDimArray模板的建構式,而是可以將其std:initializer_list作為引數,如下所示:
DynamicMultiDimArray(std::initializer_list<std::size_t> dimensions) :
std::vector<T>(std::accumulate(dimensions.begin(), dimensions.end(), std::size_t{1}, std::multiplies<std::size_t>())),
Dimensions(dimensions) {}
是的,元素的實際向量的初始化現在看起來非常難看。我建議不要為此使用繼承,而是使用組合。然后你可以先初始化維度,然后是資料的陣列/向量,并且可以在MultiDimArray其中創建一個成員變數來計算條目的數量:
template <class derived, class T>
class MultiDimArray {
...
constexpr std::size_t nEntries() const {
const auto &dimensions = Derived().dimensions;
return std::accumulate(dimensions.begin(), dimensions.end(), std::size_t{1}, std::multiplies<std::size_t>());
}
...
};
template <class T>
class DynamicMultiDimArray :
public MultiDimArray<...>
{
std::vector<std::size_t> dimensions;
std::vector<T> data;
...
public:
DynamicMultiDimArray(std::initializer_list<std::size_t> dimensions) :
Dimensions(dimensions),
data(nEntries()) {}
};
同樣,您可以為索引operator()使用 a std::initializer_list<std::size_t>。使用它的代碼如下所示:
DynamicMultiDimArray dimarr({3, 5, 7}); // create new array
...
std::cout << dimarr({1, 2, 3}) << '\n'; // print element at index 1, 2, 3
這避免了很多模板麻煩。您甚至可以考慮創建一個自定義的多維索引型別而不是 a std::initializer_list<std::size_t>,從而更容易將索引存盤在變數中并傳遞它們。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/333136.html
