如何使我的uninitialised_allocator安全
How to make my uninitialised_allocator safe?
根据这个问题,我想使用unitialised_allocator
与std::vector
一起使用,以避免在构造时元素的默认初始化(或std::vector
的resize()
)(也见这里的用例)。我现在的设计是这样的:
// 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>>;
然而,正如我的评论所指出的,我不是100%确定 static_assert()
中的适当条件是什么?(顺便说一句,可以考虑SFINAE,欢迎对此提出任何有用的评论)
显然,必须避免由于试图破坏未初始化的对象而导致的灾难。考虑
unitialised_vector< std::vector<int> > x(10); // dangerous.
有人建议(Evgeny Panasyuk的评论)我断言微不足道的可构造性,但这似乎没有抓住上面的灾难场景。我只是想检查一下clang说的关于std::is_trivially_default_constructible<std::vector<int>>
(或std::is_trivially_destructible<std::vector<int>>
),但我得到的只是clang 3.2的崩溃…
当然,我认为设计可以简化,假设一个符合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
了在
U
上模板construct
成员。如果您想将此分配器用于需要分配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)
- 从不同线程使用int64的不同字节安全吗
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 虚拟决赛作为安全
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 如何将元素添加到数组的线程安全函数?
- C++中的线程安全删除
- 通过网络、跨平台传递std::变体是否安全
- 在std::thread中,joinable()然后join()线程安全吗
- 使用std::istream::peek()总是安全的吗
- 从值小于256的uint16到uint8的Endian安全转换
- 在c++队列中使用pop和visit实现线程安全
- 在类型和包装器之间reinterpret_cast是否安全<Type>?
- 以线程安全的方式调用"QQuickPaintedItem::updateImage(const QImage&image)"(no QThread)
- 全局变量 多读取器 一个写入器多线程安全?
- 安全到标准:移动会员?
- AcquireCredentialsHandleA() 返回 PFX 文件的0x8009030e(安全包中没有可用的凭据
- 共享队列的线程安全
- boost::文件系统::recursive_directory_iterator多线程安全
- 跨 DLL 边界访问虚拟方法是否安全/可能?
- C++动态安全 2D 交错阵列