不需要其元素为默认且可复制构造的容器
Posted
技术标签:
【中文标题】不需要其元素为默认且可复制构造的容器【英文标题】:Container that doesn't require its elements to be default and copy constructible 【发布时间】:2010-07-25 13:09:55 【问题描述】:我正在寻找一个类似 C++ 容器的类,它包装了一个类型化的对象数组,这些对象不一定被初始化,也不必是默认可构造或复制构造的。这对于没有明确定义的复制语义的 RAII 对象会很有趣。这样一个类似容器的类似乎相当容易编写(使用分配器来分配未初始化的内存并放置新的)。 Boost中是否有类似的东西我刚刚忽略了?我不是在寻找std::vector
(它要求它的元素是可复制构造的)或指针容器,而是像这样的东西:
#include <cstddef>
#include <memory>
#include <vector>
#include <algorithm>
#include <iostream>
template< typename T, typename Alloc = std::allocator<T> >
class FixedVector
public:
typedef typename Alloc::value_type value_type;
typedef typename Alloc::pointer pointer;
typedef typename Alloc::reference reference;
typedef typename Alloc::const_pointer const_pointer;
typedef typename Alloc::const_reference const_reference;
typedef typename Alloc::size_type size_type;
typedef typename Alloc::difference_type difference_type;
typedef pointer iterator;
typedef const_pointer const_iterator;
explicit FixedVector(size_type size, const Alloc& allocator = Alloc()):
m_alloc(allocator),
m_size(size),
m_data(m_alloc.allocate(size)),
m_constructed(size)
FixedVector(const FixedVector& other):
m_alloc(other.m_alloc),
m_size(other.m_size),
m_data(m_alloc.allocate(m_size)),
m_constructed(other.m_constructed)
for (size_type i = 0; i != m_size; ++i)
if (m_constructed[i]) m_alloc.construct(m_alloc.address(m_data[i]), other[i]);
~FixedVector()
for (size_type i = 0; i != m_size; ++i)
if (m_constructed[i]) m_alloc.destroy(m_alloc.address(m_data[i]));
m_alloc.deallocate(m_data, m_size);
FixedVector& operator=(FixedVector other)
other.swap(*this);
return *this;
// operator[] and other unimportant stuff
void swap(FixedVector& other)
std::swap(m_alloc, other.m_alloc);
std::swap(m_size, other.m_size);
std::swap(m_data, other.m_data);
std::swap(m_constructed, other.m_constructed);
void construct(size_type index)
new (m_alloc.address(m_data[index])) T();
m_constructed[index] = true;
template<typename U>
void construct(size_type index, U& val)
new (m_alloc.address(m_data[index])) T(val);
m_constructed[index] = true;
template<typename U>
void construct(size_type index, const U& val)
new (m_alloc.address(m_data[index])) T(val);
m_constructed[index] = true;
private:
Alloc m_alloc;
size_type m_size;
pointer m_data;
std::vector<bool> m_constructed;
;
template<typename T, typename Alloc>
void swap(FixedVector<T, Alloc>& first, FixedVector<T, Alloc>& second)
first.swap(second);
namespace std
template<typename T, typename Alloc>
void swap(FixedVector<T, Alloc>& first, FixedVector<T, Alloc>& second)
first.swap(second);
class Test
public:
explicit Test(int val): m_val(val)
std::cout << "Test::Test(" << val << ')' << std::endl;
~Test()
std::cout << "Test::~Test() [with m_val = " << m_val << ']' << std::endl;
int val() const
return m_val;
private:
int m_val;
Test(const Test&);
Test& operator=(const Test&);
;
template<typename Char, typename Traits>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& stream, const Test& object)
return stream << object.val();
int main()
typedef FixedVector<Test> FVT;
FVT w(10);
w.construct(7, 7);
w.construct(2, 2);
std::cout << "w[2] = " << w[2] << std::endl;
该解决方案应该在 C++03 中工作(例如,不允许移动语义)。这个问题有点学术性——我只是想知道为什么 Boost 中似乎不存在这样的类。
【问题讨论】:
请注意,你有一个非常奇怪的operator=
。
抱歉,没有注意到参数是按值传递的。 (赋值仍然与手头的问题无关:当其存储的类型不可复制时,您不能分配/复制 FixedVector。)
你最后找到答案了吗?
【参考方案1】:
这样一个类似容器的类似乎 写起来相当容易(使用 分配器分配未初始化的 内存和位置新)。
这正是std::vector
所做的。要使用展示位置new
,您必须进行复制。
void store(const T& value)
new (storage) T(value); //<-- invokes copy constructor
也许 boost::ptr_vector 适用于不可复制的类型(你会给它指针)。
#include <boost/noncopyable.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <iostream>
struct X: boost::noncopyable
X(int x): x(x)
int x;
;
int main()
boost::ptr_vector<X> vec;
for (int i = 1; i < 10; ++i)
vec.push_back(new X(i));
for (size_t i = 0; i != vec.size(); ++i)
std::cout << vec[i].x << '\n';
在 C++0x 中,容器将接受不可复制的类型,只要它们是可移动的(这对于不可复制的类型通常应该是可实现的)。
【讨论】:
新位置并不意味着可复制性。我建议的类模板使用placement new,并且不需要类型是可复制的。 @Philipp:好的,是的。您的意思是容器将从给定的参数构造一个对象。然后你会遇到转发任意数量的参数的问题。又是 C++0x 会处理的事情。 是的,我知道转发问题(这就是为什么我在我的示例中只有零元和一元构造函数),但是有许多 Boost 库手动编写或自动编写大量参数列表- 使用预处理器或模板(例如 Boost.Tuple)生成,所以我不认为这是一个普遍的问题。拥有最多两个参数的重载已经很有帮助了。 其实有解决转发问题的 Boost.InPlaceFactory 库。似乎只缺少相应的容器类。【参考方案2】:在 C++0x 中,std::vector
的元素不必是可复制构造的,只要它们是可移动的。
【讨论】:
谢谢,但在 Boost 中是否还有不需要 C++0x 的解决方案?以上是关于不需要其元素为默认且可复制构造的容器的主要内容,如果未能解决你的问题,请参考以下文章