在this question之后,我想使用一个带有std::vector的unitialised_allocator,以避免在构造时默认初始化元素(或者std::vector的resize() )(参见here中的一个用例)。我目前的设计如下:
// based on a design by Jared Hoberock
template<typename T, typename base_allocator >
struct uninitialised_allocator : base_allocator::template rebind<T>::other
{
// added by Walter Q: IS THIS THE CORRECT CONDITION?
static_assert(std::is_trivially_default_constructible<T>::value,
"value type must be default constructible");
// added by Walter Q: IS THIS THE CORRECT CONDITION?
static_assert(std::is_trivially_destructible<T>::value,
"value type must be default destructible");
using base_t = typename base_allocator::template rebind<T>::other;
template<typename U>
struct rebind
{
typedef uninitialised_allocator<U, base_allocator> other;
};
typename base_t::pointer allocate(typename base_t::size_type n)
{
return base_t::allocate(n);
}
// catch default construction
void construct(T*)
{
// no-op
}
// forward everything else with at least one argument to the base
template<typename Arg1, typename... Args>
void construct(T* p, Arg1 &&arg1, Args&&... args)default_
{
base_t::construct(p, std::forward<Arg1>(arg1), std::forward<Args>(args)...);
}
};然后可以像这样定义一个unitialised_vector<>模板:
template<typename T, typename base_allocator = std::allocator<T>>
using uninitialised_vector =
std::vector<T,uninitialised_allocator<T,base_allocator>>;然而,正如我的评论所指出的那样,我不能百分之百地确定static_assert()__中的适当条件是什么(顺便说一句,我们可以考虑SFINAE -欢迎对此有任何有用的评论)。
显然,一个人必须避免由未初始化的物体试图进行非平凡的破坏而引发的灾难。考虑一下
unitialised_vector< std::vector<int> > x(10); // dangerous.有人建议( Evgeny的评论),我主张微不足道的可构造性,但这似乎没有抓住上述灾难场景。我只是试着检查一下clang对std::is_trivially_default_constructible<std::vector<int>> (或std::is_trivially_destructible<std::vector<int>>)说了些什么,但我得到的只是一次clang3.2的崩溃.
另一个更高级的选项是设计一个分配器,该分配程序只为这样做安全的对象设计默认结构。
发布于 2013-04-12 15:51:22
Fwiw,我认为可以简化设计,假设一个符合C++11的容器:
template <class T>
class no_init_allocator
{
public:
typedef T value_type;
no_init_allocator() noexcept {}
template <class U>
no_init_allocator(const no_init_allocator<U>&) noexcept {}
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(static_cast<void*>(p));}
template <class U>
void construct(U*) noexcept
{
static_assert(std::is_trivially_default_constructible<U>::value,
"This allocator can only be used with trivally default constructible types");
}
template <class U, class A0, class... Args>
void construct(U* up, A0&& a0, Args&&... args) noexcept
{
::new(up) U(std::forward<A0>(a0), std::forward<Args>(args)...);
}
};allocator_traits处理rebind了。construct上模板U成员。如果您想在需要分配T以外的东西的容器(例如std::list)中使用这个分配器,这会有所帮助。static_assert测试移到重要的单个construct成员中。您仍然可以创建一个using
template <class T>
using uninitialised_vector = std::vector<T, no_init_allocator<T>>;但这仍然无法编译:
unitialised_vector< std::vector<int> > x(10);
test.cpp:447:17: error: static_assert failed "This allocator can only be used with trivally default constructible types"
static_assert(std::is_trivially_default_constructible<U>::value,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~我认为对is_trivially_destructible的测试太过分了,除非您还优化了destroy以不做任何事情。但我没有看到这样做的动机,因为我认为,无论如何,它应该得到优化,无论何时适当。如果没有这样的限制,你可以:
class A
{
int data_;
public:
A() = default;
A(int d) : data_(d) {}
};
int main()
{
uninitialised_vector<A> v(10);
}它只是起作用了。但是,如果您让~A()变得非常重要:
~A() {std::cout << "~A(" << data_ << ")\n";}然后,至少在我的系统中,您在构造上出现了一个错误:
test.cpp:447:17: error: static_assert failed "This allocator can only be used with trivally default constructible types"
static_assert(std::is_trivially_default_constructible<U>::value,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~也就是说,如果A有一个非平凡的析构函数,它就不再是琐碎的可构造的。
但是,即使使用非平凡的析构函数,仍然可以:
uninitialised_vector<A> v;
v.push_back(A());这是可行的,只适用于,因为我并没有过分地要求一个简单的析构函数。在执行这个命令时,我让~A()按预期运行:
~A(0)
~A(0)https://stackoverflow.com/questions/15967293
复制相似问题