【发布时间】:2019-01-05 11:04:30
【问题描述】:
我正在根据 2018 年后圣地亚哥草案 (N4791) 实施我自己的向量,并且对实施强大的异常安全有一些疑问。
这里有一些代码:
template <typename T, typename Allocator>
void Vector<T, Allocator>::push_back(const T& value)
{
if (buffer_capacity == 0)
{
this->Allocate(this->GetSufficientCapacity(1));
}
if (buffer_size < buffer_capacity)
{
this->Construct(value);
return;
}
auto new_buffer = CreateNewBuffer(this->GetSufficientCapacity(
buffer_size + 1), allocator);
this->MoveAll(new_buffer);
try
{
new_buffer.Construct(value);
}
catch (...)
{
this->Rollback(new_buffer, std::end(new_buffer));
throw;
}
this->Commit(std::move(new_buffer));
}
template <typename T, typename Allocator>
void Vector<T, Allocator>::Allocate(size_type new_capacity)
{
elements = std::allocator_traits<Allocator>::allocate(allocator,
new_capacity);
buffer_capacity = new_capacity;
}
template <typename T, typename Allocator> template <typename... Args>
void Vector<T, Allocator>::Construct(Args&&... args)
{
// TODO: std::to_address
std::allocator_traits<Allocator>::construct(allocator,
elements + buffer_size, std::forward<Args>(args)...);
++buffer_size;
}
template <typename T, typename Allocator>
Vector<T, Allocator> Vector<T, Allocator>::CreateNewBuffer(
size_type new_capacity, const Allocator& new_allocator)
{
Vector new_buffer{new_allocator};
new_buffer.Allocate(new_capacity);
return new_buffer;
}
template <typename T, typename Allocator>
void Vector<T, Allocator>::Move(iterator first, iterator last, Vector& buffer)
{
if (std::is_nothrow_move_constructible_v<T> ||
!std::is_copy_constructible_v<T>)
{
std::move(first, last, std::back_inserter(buffer));
}
else
{
std::copy(first, last, std::back_inserter(buffer));
}
}
template <typename T, typename Allocator
void Vector<T, Allocator>::MoveAll(Vector& buffer)
{
Move(std::begin(*this), std::end(*this), buffer);
}
template <typename T, typename Allocator>
void Vector<T, Allocator>::Rollback(Vector& other, iterator last) noexcept
{
if (!std::is_nothrow_move_constructible_v<T> &&
std::is_copy_constructible_v<T>)
{
return;
}
std::move(std::begin(other), last, std::begin(*this));
}
template <typename T, typename Allocator>
void Vector<T, Allocator>::Commit(Vector&& other) noexcept
{
this->Deallocate();
elements = other.elements;
buffer_capacity = other.buffer_capacity;
buffer_size = other.buffer_size;
allocator = other.allocator;
other.elements = nullptr;
other.buffer_capacity = 0;
other.buffer_size = 0;
}
我发现此代码存在 2 个问题。我尝试遵循std::move_if_noexcept 逻辑,但如果元素不可移动构造但allocator_traits::construct 在自定义分配器内的一些日志记录代码中抛出异常怎么办?然后我的MoveAll 调用将抛出并仅产生基本保证。这是标准的缺陷吗? Allocator::construct是否应该有更严格的措辞?
还有一个Rollback。只有当被移动的元素不能被移动分配时,它才会真正产生强有力的保证。否则,再次,只有基本保证。这是应该的吗?
【问题讨论】:
-
论文说: 注意:这是初稿。众所周知,它是不完整和不正确的,并且有很多错误的格式
-
所以也许我们应该在 C++20 发布之前修复它?
-
是的,审查文档的人越多越好。这个论坛是突出这些问题的好地方。
-
@Damian 是的,这是 C++ 标准的工作草案。
-
先构造值,再移动元素!
标签: c++ containers c++20 exception-safety