C++ C++;隐式不可移动类的对象重定位方法

C++ C++;隐式不可移动类的对象重定位方法,c++,move,copy-constructor,move-semantics,mutable,C++,Move,Copy Constructor,Move Semantics,Mutable,我正在编写一些自定义库,比如stl,但是在构造函数中没有分配,在资源拥有类中禁用了复制构造函数(因为环境不支持异常,堆上的所有alloc都需要通过retcode检查) 所以我正在从移植btree,由于代码问题和我的方法相结合而陷入困境 和所有stl实现一样,value_type是std::pairconst限定符使整个对隐式不可移动 So代码 x->construct_value(j, std::move(fields_.values[i])); () 实际上,不移动对象(返回t&而不是

我正在编写一些自定义库,比如stl,但是在构造函数中没有分配,在资源拥有类中禁用了复制构造函数(因为环境不支持异常,堆上的所有alloc都需要通过retcode检查)

所以我正在从移植btree,由于代码问题和我的方法相结合而陷入困境

和所有stl实现一样,value_type是std::pairconst限定符使整个对隐式不可移动

So代码

x->construct_value(j, std::move(fields_.values[i]));
()

实际上,不移动对象(返回t&而不是t&&),并且

new(v)value_类型(std::forward)

正确地说,无法通过复制构造函数构造pair

有没有一种方法可以在内存中重新定位对象而不需要或绕过复制-移动语义?当然,简单的解决方法是使用可变键创建std::pair,但这并不完全相同

我发现阿瑟·奥德怀尔提出的“微不足道的可撤销”的建议,我得出的结论是,它与本案完全相关。 ()

亚瑟·奥德怀尔在这里!:)

实际上,在标准C++中没有任何方法可以实现你想要的。事实上,在土地上也没有办法做你想做的事。基本上,你有这样一种类型:

注意,我在这里内联了
x->construct\u value
destroy\u value
内容。在现实生活中,我可能会把这些东西重新编成助手。P1144将此帮助程序命名为std::relocate_at

// Relocate value i in this node to value j in node x.
void relocate_value(int i, btree_node* x, int j) {
    assert(x != this || i != j);
    assert(0 <= i && i <= fields_.count);
    assert(0 <= j && j <= x->fields_.count);
    my::relocate_at(&fields_.values[i], &x->fields_.values[j]);
}
请注意(类模板的,而不是变量模板的)的额外参数。这让我们可以编写定制,如

template<class A, class B, class = std::enable_if_t<my::is_trivially_relocatable_v<std::remove_const_t<A>> && my::is_trivially_relocatable_v<std::remove_const_t<B>>>>
struct is_trivially_relocatable<std::pair<A, B>> : std::true_type {};

template显然不是。您要么尊重该语言的复制/移动语义,要么脱离该语言。你可以用标准的计算机科学方法来解决这个问题:添加一层间接层。1) 让容器键成为指向真实键的指针。2) 将资源持有类更改为持有实际受限资源的实例的句柄(即不透明指针)。(实际上,这两个类型是相同的,以防万一#2你给句柄起了一个名字,而不仅仅是使用裸指针。)“就像今天不可能生成一个编译器可以识别为“可复制”的类型,而它也不能被复制分配”嗯。。。你真的可以做到。出于某些原因,普通可复制性规则只要求至少一个复制/移动构造函数/赋值运算符是普通的;其他的可以被删除,并且类型仍然是可复制的。
// Relocate value i in this node to value j in node x.
void relocate_value(int i, btree_node* x, int j) {
    assert(x != this || i != j);
    assert(0 <= i && i <= fields_.count);
    assert(0 <= j && j <= x->fields_.count);
    if constexpr (my::is_trivially_relocatable_v<value_type>) {
        memcpy(&x->fields_.values[j], &fields_.values[i], sizeof(value_type));
    } else {
        ::new (&x->fields_.values[j]) value_type(std::move(fields_.values[i]));
        fields_.values[i].~value_type();
    }
}
// Relocate value i in this node to value j in node x.
void relocate_value(int i, btree_node* x, int j) {
    assert(x != this || i != j);
    assert(0 <= i && i <= fields_.count);
    assert(0 <= j && j <= x->fields_.count);
    my::relocate_at(&fields_.values[i], &x->fields_.values[j]);
}
template<class T, class = void>
struct is_trivially_relocatable : std::is_trivially_copyable<T> {};
template<class T>
inline constexpr bool is_trivially_relocatable_v = is_trivially_relocatable<T>::value;
template<class A, class B, class = std::enable_if_t<my::is_trivially_relocatable_v<std::remove_const_t<A>> && my::is_trivially_relocatable_v<std::remove_const_t<B>>>>
struct is_trivially_relocatable<std::pair<A, B>> : std::true_type {};