在可能重叠的范围之间复制类

Jos*_*een 6 c++

在C中,我们有功能memcpymemmove有效地复制数据.如果源和目标区域重叠,前者产生未定义的行为,但后者保证"按预期"处理,可能通过注意重叠的方向和(如果需要)选择不同的算法.

当然,上述函数在C++(as std::memcpystd::memmove)中可用,但它们并不适用于非平凡的事情class.相反,我们得到std::copystd::copy_backward.如果源和目标范围不重叠,则每个都有效; 而且,每个都保证适用于重叠的一个"方向".

如果我们想要从一个区域复制到另一个区域,我们可以使用什么?我们在编译时不知道范围是否可能重叠或者可能出现重叠的方向? 我们似乎没有选择.对于一般情况iterator,可能很难确定范围是否重叠,所以我理解为什么在这种情况下没有提供解决方案,但是当我们处理指针时呢? 理想情况下,有一个功能,如:

template<class T>
T * copy_either_direction(const T * inputBegin, const T * inputEnd, T * outputBegin) {
    if ("outputBegin ? [inputBegin, inputEnd)") {
        outputBegin += (inputEnd - inputBegin);
        std::copy_backward(inputBegin, inputEnd, outputBegin);
        return outputBegin;
    } else {
        return std::copy(inputBegin, inputEnd, outputBegin);
    }
}
Run Code Online (Sandbox Code Playgroud)

(T *替换的类似功能std::vector<T>::iterator也会很好.如果保证能够正常工作inputBegin == outputBegin,那就更好了,但这是我的另一个抱怨.)

不幸的是,我没有看到在if语句中写入条件的合理方法,因为将指针与单独的内存块进行比较通常会产生未定义的行为.另一方面,实施显然有自己的方法来实现这一点,因为std::memmove本身就需要一个.因此,任何实现都可以提供这样的功能,从而满足程序员根本不能的需要.既然std::memmove被认为是有用的,为什么不copy_either_direction呢?我缺少一个解决方案吗?

Nic*_*las -1

如果我们想要从一个区域复制到另一个区域并且我们在编译时不知道范围是否可能重叠或重叠可能发生在什么方向,我们可以使用什么?

这不是一个逻辑上一致的概念。

复制操作后,您将拥有两个对象。每个对象都由单独且不同的内存区域定义。您不能拥有以这种方式重叠的对象(您可以拥有子对象,但对象类型不能是其自己的子对象)。因此,不可能将对象复制到其自身的一部分之上。

将对象移动到其自身之上在逻辑上也是不一致的。为什么?因为移动在 C++ 中是虚构的;搬家后,你仍然拥有两个功能完美的物品。移动操作只是一种破坏性复制,它会窃取另一个对象拥有的资源。它仍然在那里,而且仍然是一个可行的物体。

由于一个物体不能与另一个物体重叠,所以这也是不可能的。

普通可复制类型可以解决这个问题,因为它们只是位块,没有析构函数或专门的复制操作。所以他们的寿命并不像其他人那么严格。非平凡可复制类型无法执行此操作,因为:

memmove 的经验表明,在这种情况下可能有一个解决方案(也许也适用于进入连续容器的迭代器)。

对于 C++ 中不可简单复制的类型来说,这既不可能也不通常是可取的。

平凡可复制性的规则是该类型没有非平凡的复制/移动构造函数/赋值运算符,也没有非平凡的析构函数。一个简单的复制/移动构造函数/赋值只不过是一个memcpy,而一个简单的析构函数什么也不做。因此,这些规则有效地确保类型只不过是“位块”。并且一个“位块”与另一个“位块”没有什么不同,因此通过复制它memmove是一种合法的构造。

如果一个类型有一个真正的析构函数,那么该类型就会维护某种需要实际努力来维护的不变量。它可能会释放指针或释放文件句柄或其他什么。鉴于此,复制这些位是没有意义的,因为现在有两个引用相同指针/文件句柄的对象。这是一件坏事,因为班级通常想要控制它的处理方式。

如果类本身不参与复制操作,就没有办法解决这个问题。不同的阶级在管理其内部方面有不同的行为。事实上,这就是具有复制构造函数和赋值运算符的对象的全部目的。这样一个阶级就可以自己决定如何维持自己国家的理智。

它甚至不必是指针或文件句柄。也许每个类实例都有一个唯一的标识符;这样的值是在构造时生成的,并且永远不会被复制(新副本获得新值)。如果您违反此类限制,将使memmove您的程序处于不确定状态,因为您将拥有期望此类标识符是唯一的代码。

这就是为什么memmove对非平凡可复制类型进行 ing 会产生未定义的行为。