假设您有一个对向量进行操作的类:
class Foo{
public:
Foo() {
m_dynamic_data.push_back(5);
std::cout << m_dynamic_data[0] << std::endl;
}
private:
std::vector<int> m_dynamic_data;
};
在我的情况下,该类非常庞大,需要增加2500行代码。
此类的行为是动态的(因此std::vector
)。但我也想提供一个“静态”实现(使用std::array
)。因此,添加了std::size_t N
,它现在应该控制何时使用哪个属性。
template<std::size_t N>
class Foo{
private:
std::vector<int> m_dynamic_data; //use this, when N == 0
std::array<int, N> m_static_data; //use this, when N != 0
};
我不确定是否可以使它正常工作。使用#define
不会完成这项工作(因为它无法更改)。 constexpr
也不能被两个属性包围。最好的解决方案可能是提供一个基类,然后从中继承动态和静态的情况。但是在我接下来的几天里这样做之前,我想知道是否还没有一种技术。
我考虑过将两者都放入std::unique_ptr
中,并且只构造相关的数组:
template<std::size_t N>
class Foo {
public:
Foo() {
if constexpr (N) {
m_static_data_ptr = std::make_unique<std::array<int, N>>();
(*m_static_data_ptr)[0] = 5;
std::cout << (*m_static_data_ptr)[0] << std::endl;
}
else {
m_dynamic_data_ptr = std::make_unique<std::vector<int>>(1);
(*m_dynamic_data_ptr)[0] = 5;
std::cout << (*m_dynamic_data_ptr)[0] << std::endl;
}
}
private:
std::unique_ptr<std::vector<int>> m_dynamic_data_ptr;
std::unique_ptr<std::array<int, N>> m_static_data_ptr;
};
我之前曾问过这种情况here。但这显然不是一个好方法。 (碎片存储,高速缓存未命中率)。 std::optional
似乎也很有趣,但是它将sizeof(Foo)推到了我的目标太远了。
最终还使用了void
指针:
template<std::size_t N>
class Foo {
public:
Foo() {
if constexpr (N) {
m_data = malloc(sizeof(std::array<int, N>));
(*static_cast<std::array<int, N>*>(m_data))[0] = 5;
std::cout << (*static_cast<std::array<int, N>*>(m_data))[0] << std::endl;
}
else {
m_data = new std::vector<int>;
(*static_cast<std::vector<int>*>(m_data)).push_back(5);
std::cout << (*static_cast<std::vector<int>*>(m_data))[0] << std::endl;
}
}
~Foo() {
delete[] m_data;
}
private:
void* m_data;
};
但这似乎很脏[...] 因此,目标是在编译时使用任一数组结构。感谢您的帮助/建议!
答案 0 :(得分:4)
您可以将Installer.RelatedProducts
的 data 部分抽象到另一个类模板。
Foo
您必须将成员函数添加到template<std::size_t N> struct FooData
{
std::array<int, N> container;
}
template <> struct FooData<0>
{
std::vector<int> container;
}
template<std::size_t N>
class Foo{
private:
using DataType = FooData<N>;
DataType data;
};
中以支持其他抽象。此类功能及其接口的数量取决于您在FooData
中使用容器的方式。
答案 1 :(得分:2)
R Sahu的答案很好,但是您不需要通过结构间接访问容器。
template<std::size_t N>
struct FooData { using type = std::array<int, N>;};
template <>
struct FooData<0> { using type = std::vector<int>; };
template<std::size_t N>
using FooData_t = typename FooData<N>::type;
template<std::size_t N>
class Foo{
private:
FooData_t<N> data;
};
或者,您也可以使用std::conditional_t
:
template<std::size_t N>
class Foo{
private:
std::conditional_t<N==0, std::vector<int>, std::array<int, N>> data;
};
答案 2 :(得分:1)
您可能希望将此动态/静态“变形”与巨型2500行Foo
类的其余部分隔离开来。我可以想象在std::array
周围有一个小的包装程序来模仿std::vector
的界面。它可以用作Foo
的成员。如果将静态容量设置为前哨值0
,则可以将其专门用于仅从实数std::vector
中得出:
#include <cassert>
#include <cstddef>
#include <array>
#include <iostream>
#include <vector>
template<class value_type_, std::size_t capacity_>
struct StaticOrDynamic {
using value_type = value_type_;
static constexpr std::size_t capacity = capacity_;
std::array<value_type, capacity> arr_{};
std::size_t size_{0};
constexpr void push_back(const value_type& x) {
assert(size_ < capacity && "must not exceed capacity");
arr_[size_++] = x;
}
constexpr const value_type_& at(std::size_t i) const {
assert(i < size_ && "must be in [0, size)");
return arr_[i];
}
/* other members etc */
};
template<class value_type_>
struct StaticOrDynamic<value_type_, 0>// specialization for dynamic case
: std::vector<value_type_>
{
using std::vector<value_type_>::vector;
};
template<std::size_t capacity_>
struct Foo {
static constexpr std::size_t capacity = capacity_;
StaticOrDynamic<int, capacity> m_data_{};
Foo() {// static version may be constexpr (without debug output)
m_data_.push_back(5);
std::cout << m_data_.at(0) << std::endl;
}
};
int main() {
Foo<5> static_foo{};
Foo<0> dynamic_foo{};
}
类似的行为(由模板参数选择的静态/动态)is offered in, e.g., the Eigen library。我不知道它在那里如何实现。