使用 std::vector 时如何将索引信息传递给元素构造函数?
Posted
技术标签:
【中文标题】使用 std::vector 时如何将索引信息传递给元素构造函数?【英文标题】:How to pass index information to element constructor when using std::vector? 【发布时间】:2019-11-18 09:27:12 【问题描述】:有什么方法可以将向量的索引传递给它的元素的构造函数? 例如:
#include <iostream>
#include <vector>
class Foo
public:
Foo(unsigned long index)
std::cout << index << std::endl;
;
int main()
std::vector<Foo> foo;
foo.resize(2); // any way to make this work?
此代码确实不起作用,因为编译器不知道如何构造Foo(unsigned long index)
,但我可以做一些技巧(例如自定义分配器?)以使此代码实际工作?
【问题讨论】:
如果没有兼容的 default-ctor,不,没有办法让 that 工作。Foo
只能使用提供的 ctor-argument 构造。然而,我很好奇,这个解决方案试图解决的真正问题是什么,因为有可能为那个提供更合适的解决方案,而不是试图让这个工作。
@WhozCraig 我使用与此类似的代码通过向量中的“ID”(=index)查找类 Foo 对象。
@WhozCraig 我的元素是一个特殊的数据结构,恰好与索引有关系,我只是好奇我是否可以这样做,所以我问:)
@reavenisadesk 我明白了。取决于Foo
来自何处的性质以及它们的管理方式(是否有不止一个foo
在各个地方浮动?Foo
可以从一个foo
移动到另一个吗?当Foo
从foo
中删除 并且此后所有后续索引不再对齐时,期望是什么?),可能值得考虑另一种身份映射。无论如何,感谢您的澄清。
【参考方案1】:
您可以在 for 循环中添加元素并将索引作为参数传递给它们的 ctor,如下所示:
// Init your vector + optionally reserve space
std::vector<Foo> foo;
const unsigned elements_to_add = 5; // or whatever number
foo.reserve(foo.size() + elements_to_add);
// foo.size() will be passed as parameter to the ctor you defined
for (std::size_t i = 0; i < elements_to_add; i++)
foo.emplace_back(foo.size());
【讨论】:
但是 foo.resize(2);没有给出想要的结果。 @MFnx 好吧,不必使用调整大小来填充数组。【参考方案2】:不,您需要使用 std::generate()
或 std::generate_n()
与 std::back_inserter()
结合使用。
【讨论】:
可能更多std::generate_n
,std::back_inserter
。【参考方案3】:
您可以编写一个自定义的有状态分配器,该分配器将在构造对象时传递一个索引。
小例子:
template<class T>
class Allocator
public:
using value_type = T;
T* allocate(std::size_t n)
return static_cast<T*>(::operator new(n * sizeof(T)));
void deallocate(T* p, std::size_t) noexcept
::operator delete(p);
template<class... Args>
void construct(T* p, Args&&... args)
::new(static_cast<void*>(p)) T(counter_++, std::forward<Args>(args)...);
void destroy(T* p) noexcept
p->~U();
--counter_;
private:
std::size_t counter_ = 0;
;
使用示例:
struct Foo
Foo(std::size_t index)
std::cout << index << ' ';
Foo(std::size_t index, const Foo& other) : Foo(other)
std::cout << index << ' ';
;
std::vector<Foo, Allocator<Foo>> foos1;
foos1.resize(3);
std::cout << std::endl;
std::vector<Foo, Allocator<Foo>> foos2;
foos2.resize(4);
// Output:
// 0 1 2
// 0 1 2 3
【讨论】:
但似乎我们不能保证 counter_ 是索引,尽管我还不能举出一个例子。但是你给了我一个想法,谢谢 @reavenisadesk,此解决方案依赖于调用resize()
时对象构造的特定顺序。标准只是说resize()
“追加”(n - size())
元素到序列中。 libstdc++
做了这样的事情:for (; n > 0; --n, ++cur) traits::construct(alloc, addressof(*cur));
。但是 AFAIK,标准不保证这个顺序。因此,这个答案更像是一个技巧,而不是可以在实际代码中使用的解决方案。 nada 给出了一个可靠的解决方案。【参考方案4】:
我想有很多方法可以或多或少地得到你想要的。但迟早你可能会发现你不需要这个。
这是一个可能的解决方案:
#include <iostream>
#include <vector>
class Foo
inline static unsigned long _static_index = 0;
unsigned long _index;
public:
Foo() : _index(_static_index) ++_static_index;
auto index() const return _index;
static void resetIndex() _static_index = 0;
;
int main()
std::vector<Foo> foos;
Foo::resetIndex();
foos.resize(2);
for (const auto& f : foos)
std::cout << f.index() << std::endl;
return 0;
因此,您只需增加一个静态计数器并将其分配给私有成员 _index
。这显然有其局限性。例如,假设您在填写 Foo
s 的 vector
之前创建了 3 个 Foo
实例,那么 foos[0].index()
将返回 3 而不是 0。因此,在填写 foos
之前,您需要重置 @ 987654328@。
【讨论】:
如果要放入 Foos 的数组不止 1 个,这将行不通。 另外不要忘记内联_static_index,否则你必须在其他地方初始化它。并且以 _ 开头的变量名称不是很 C-plus-plussy。 @nada 我在 C++ 中看到了很多_someMember
。这当然不是“不是很 C+plussy”。
@nada 不,这行不通。明显的限制......但是,根据问题, foos.resize(2) 应该可以工作。
@nada 1. 下划线 + 小写字母对于全局命名空间之外的标识符(即类成员)是可以的,所以这是 100% 合法的,2. 这不是我的任何选择我见过的地方。以上是关于使用 std::vector 时如何将索引信息传递给元素构造函数?的主要内容,如果未能解决你的问题,请参考以下文章
将数据从 std::vector 传递到 std::valarray 的最有效方法
通过引用传递的 std::vector 不是从函数传递到 main()