【发布时间】:2013-02-15 12:47:03
【问题描述】:
是否有用于固定长度序列的标准容器,其中该长度在运行时确定。最好,我想将一个参数传递给每个序列元素的构造函数,并使用该参数来初始化一个 const 成员(或引用)。我还想在 O(1) 中的给定索引处获取序列元素。在我看来,我的所有要求都无法同时满足。
- 我知道
std::array具有固定长度,但必须在编译时知道该长度。 -
std::vector具有动态大小,并允许使用emplace传递构造函数参数。尽管您可以reserve内存来避免实际的重新分配,但类型仍然必须是movable 才能在理论上允许这样的重新分配,例如阻止 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());
}
【问题讨论】:
-
@leftaroundabout 不,我想他只是想要像 vector 这样永远不会重新定位其存储的东西(并且该属性必须是 static,即在编译时已知)。
-
向量只有在增长时才会重新定位其存储空间。因此,如果您在运行时(足够早)知道需要存储多少对象,那应该不是问题。
-
@MatsPetersson 代码必须先编译。如果没有可移动类型,则无法编译对 push_back 或 emplace_back 的调用。 (如果这些路径永远不会被命中也没关系;死代码仍然需要编译)
-
@cschwan,我非常非常有兴趣看到这个在玩具示例中起作用。我看不出编译器如何在编译时确定不需要调整大小,除非它正在执行我认为不可能的级别的非常重的优化。
-
valarray<T>的要求隐藏在第 26.2 节中,包括类型 T 的复制构造函数和赋值运算符。
标签: c++ c++11 containers sequence std