固定动态大小的容器
Container of fixed dynamic size
是否有固定长度序列的标准容器,其中长度在运行时确定。最好,我希望将一个参数传递给每个序列元素的构造函数,并使用该参数初始化const成员(或引用)。我还想获得O(1)中给定索引处的序列元素。在我看来,我的所有要求都不可能同时得到满足。
- 我知道
std::array
有固定的长度,但这个长度必须在编译时知道 std::vector
具有动态大小,并允许使用emplace
传递构造函数参数。尽管您可以reserve
内存来避免实际的重新分配,但该类型仍然必须是可移动的,才能在理论上允许这种重新分配,例如防止const成员- 然后是
std::list
和std::forward_list
,它们不需要可移动类型,但仍然可以调整大小,并且在随机访问模式下表现相当差。我还觉得,由于每个列表节点可能会被单独分配,因此与此类列表相关的开销可能相当大 - 奇怪的是,
std::valarray
是我迄今为止最好的选择,因为它有固定的长度,不会自动调整大小。尽管有一个resize
方法,但除非您实际调用该方法,否则您的类型不必是可移动的。这里的主要缺陷是缺少自定义构造函数参数,所以用这种方法初始化const成员是不可能的
我错过了其他选择吗?有没有办法调整其中一个标准容器,使其满足我的所有要求?
编辑:要让您更准确地了解我要做的事情,请参阅以下示例:
class A {
void foo(unsigned n);
};
class B {
private:
A* const a;
const unsigned i;
public:
B(A* aa) : a(aa), i(0) { }
B(A* aa, unsigned ii) : a(aa), i(ii) { }
B(const std::pair<A*, unsigned>& args) : B(args.first, args.second) { }
B(const B&) = delete;
B(B&&) = delete;
B& operator=(const B&) = delete;
B& operator=(B&&) = delete;
};
void A::foo(unsigned n) {
// Solution using forward_list should be guaranteed to work
std::forward_list<B> bs_list;
for (unsigned i = n; i != 0; --i)
bs_list.emplace_front(std::make_pair(this, i - 1));
// Solution by Arne Mertz with single ctor argumen
const std::vector<A*> ctor_args1(n, this);
const std::vector<B> bs_vector(ctor_args1.begin(), ctor_args1.end());
// Solution by Arne Mertz using intermediate creator objects
std::vector<std::pair<A*, unsigned>> ctor_args2;
ctor_args2.reserve(n);
for (unsigned i = 0; i != n; ++i)
ctor_args2.push_back(std::make_pair(this, i));
const std::vector<B> bs_vector2(ctor_args2.begin(), ctor_args2.end());
}
理论上vector
具有您需要的属性。正如您所指出的,如果元素不可编辑和/或不可分配,则不支持可能对包含的类型进行赋值的操作,尤其是任何序列修改(empace_back、push_back、insert等)。因此,要创建一个不可压缩元素的向量,必须在向量构建过程中构建每个元素。
正如Steve Jessop在他的回答中指出的那样,如果你一开始就定义向量const,你甚至无法调用这样的修改操作——当然,元素也保持不变。
如果我理解正确的话,你只有一个构造函数参数序列,而不是真正的对象序列。如果它只有一个参数,并且包含的类型有相应的构造函数,那么事情应该很容易:
struct C
{
const int i_;
C(int i) : i_(i) {}
};
int main()
{
const std::vector<C> theVector { 1, 2, 3, 42 };
}
如果构造函数是显式的,则必须首先列出一个列表,或者显式地构造初始值设定项列表中的对象:
int main()
{
auto list = { 1, 2, 3, 4 };
const std::vector<C> theVector (std::begin(list), std::end(list));
const std::vector<C> anotherVector { C(1), C(44) };
}
如果每个构造的对象不止一个参数,那么考虑一个中间创建者对象:
struct C
{
const int i_;
C(int i, int y) : i_(i+y) {}
};
struct CCreator
{
int i; int y;
explicit operator C() { return C(i,y); }
};
int main()
{
const std::vector<CCreator> ctorArgs = { {1,2}, {3,42} };
const std::vector<C> theVector { begin(ctorArgs), end(ctorArgs) };
}
我认为const std::vector<T>
具有您所要求的属性。它的元素实际上并不是用const
定义的,但它提供了它们的常量视图。你不能改变尺寸。您不能调用任何需要T
可移动的成员函数,因此对于正常使用,它们不会被实例化(如果您做了extern
类声明,它们就会被实例化,所以您不能这样做)。
如果我错了,而你确实因为T
不可移动而遇到了麻烦,那就试试const std::deque<T>
。
困难在于构造blichter——在C++11中,您可以使用初始值设定项列表来实现这一点,或者在C++03中,您也可以从非常量向量或任何其他可以获得迭代器的东西来构造const vector
。这并不一定意味着T
需要是可复制的,但确实需要有一种可以构建它的类型(也许是你为此目的发明的类型)。
使用std::shared_ptr
添加间接级别。共享指针可以像往常一样复制和分配,但不需要修改所指向的对象。这样你就不会有任何问题,如下例所示:
class a
{
public:
a(int b) : b(b) { }
// delete assignment operator
a& operator=(a const&) = delete;
private:
// const member
const int b;
};
// main
std::vector<std::shared_ptr<a>> container;
container.reserve(10);
container.push_back(std::make_shared<a>(0));
container.push_back(std::make_shared<a>(1));
container.push_back(std::make_shared<a>(2));
container.push_back(std::make_shared<a>(3));
另一个优点是函数std::make_shared
,它允许您创建具有任意数量参数的对象。
编辑:
正如MvG所指出的,也可以使用std::unique_ptr
。使用boost::indirect_iterator
,可以通过将元素复制到一个新的向量中来消除间接性:
void A::foo(unsigned n)
{
std::vector<std::unique_ptr<B>> bs_vector;
bs_vector.reserve(n);
for (unsigned i = 0; i != n; ++i)
{
bs_vector.push_back(std::unique_ptr<B>(new B(this, i)));
}
typedef boost::indirect_iterator<std::vector<std::unique_ptr<B>>::iterator> it;
// needs copy ctor for B
const std::vector<B> bs_vector2(it(bs_vector.begin()), it(bs_vector.end()));
// work with bs_vector2
}
我也遇到了这个问题,我的代码中的用例是提供线程安全向量,元素编号是固定的,并且是原子号。我已经阅读了这里所有的好答案。我想我们也可以考虑我的解决方案:
只需继承std::vector
并隐藏push_back
、emplace_back
、erase
等修饰符,就可以得到一个固定大小的向量。我们只能使用operator []
访问和修改元素。
template <typename T>
class FixedVector : protected std::vector<T> {
public:
using BaseType = std::vector<T>;
FixedVector(size_t n) : BaseType(n) {}
FixedVector(const T &val, size_t n) : BaseType(val, n) {}
typename BaseType::reference operator[](size_t n) {
return BaseType::operator[](n);
}
};
- std::向量与传递值的动态数组
- 在c++中用vector填充一个简单的动态数组
- C++中的动态铸造故障
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 内联映射初始化的动态atexit析构函数崩溃
- 使用动态分配的数组会导致代码分析发出虚假的C6386缓冲区溢出警告
- 控制允许动态运行c++的并发操作数
- 如何将这个C++哈希表转换为动态扩展和收缩,而不是使用硬设置的最大值
- 在调用FreeLibrary后,释放动态链接到具有相同版本的CRT堆的DLL的内存
- 输出没有重复元素的动态数组(收缩数组)C++
- C++为线程工作动态地分割例程
- 正在插入动态数组
- 在c++中使用动态分配的问题
- C++中的动态对象与非动态对象
- 如何在动态数组上使用搜索函数
- 视觉studo 2019中的漫画和静态/动态绑定
- 从C++中的数字输入动态创建矩阵
- 如何从QToolBox中动态创建的QLineEdit中获取文本
- C++ 动态数组每次添加时将大小增加 1 - 错误
- 在以唯一ptr为值的C++映射中,动态内存何时会被销毁