将移动语义添加到包含POD和非POD成员的现有结构

时间:2014-01-09 09:25:17

标签: c++ c++11 move-semantics

假设我有以下结构:

struct Test
{
    int n1;               // represents POD types, in real program
    int n2;               // I have much more data
    char str[STR_SIZE];
    vector<int> v;        // represents non-POD types
};

现在我向它添加移动语义:

struct Test
{
    int n1;
    int n2;
    char str[STR_SIZE];
    vector<int> v;

    Test(){}

    Test(const Test& other) : v(other.v)
    {
        Copy(other);
    }

    Test(Test&& other) : v(move(other.v))
    {
        Copy(other);
    }

    Test& operator=(const Test& other)
    {
        if ( this != &other )
        {
            Copy(other); v = other.v;
        }
        return *this;
    }

    Test& operator=(Test&& other)
    {
        if ( this != &other )
        {
            Copy(other); v = move(other.v);
        }
        return *this;
    }

private:
    void Copy(const Test& other)        // copy POD members
    {
        n1 = other.n1; n2 = other.n2;
        memcpy(str, other.str, sizeof(str));
    }
};

我对Copy函数的性能不满意,该函数指定所有非POD结构成员。是否有更好的方法来进行这种转变?我需要获得最大的性能(这种结构保存在实际程序中的STL容器中)和标准符合性。我只能考虑将所有POD成员放在开头,并使用单memcpy次调用而不是Copy函数复制它们,但这看起来像是黑客。

目前我正在使用VS2010编译器。

2 个答案:

答案 0 :(得分:1)

您可以将所有POD成员合并到一个结构中并使用pimpl idiom,这样它们也可以一起移动。

答案 1 :(得分:0)

move语义在有效移动状态时是有效的。

这是一种循环,但是如果您的状态是结构中“本地”拥有的大量二进制数据,则移动需要在目标结构中复制此二进制数据。

std::vector本地不拥有大量二进制数据。相反,它拥有一个连续的堆分配缓冲区。此缓冲区可以更改所有权,因此move语义非常有效。

当你可以节省工作时,

move语义也很有用。当move std::shared_ptr时,它可以避免对引用计数数据进行原子递增/递减。这可以防止争用(所有原子操作导致争用),增加局部性(无堆访问),即使它修改更多数据而不是copy,也是值得的。

如果您需要高效的move语义,请将大量二进制数据移动到堆中。轻松完成此操作的一种方法是存储std::unique_ptr<POD_data>并在请求副本时手动复制它(需要堆分配)。另一种方法是std::shared_ptr<POD_data>,并且具有某种写时复制访问技术。

但请注意,许多类型的数据实际上并不需要复制语义。实现std::unique_ptr<POD_data>版本,阻止复制构造函数。提供一种方法MakeCopy,可以为您实际想要复制大块内存的罕见情况生成副本。这样可以轻松有效地使用您的结构,并且使用效率低,难以追踪。

您必须通过条件保护对POD_data的访问权限,或接受move d-from版本或数据的堆分配。我建议一个返回POD_data&的方法来检查unique_ptr是否有效,如果没有,则分配一个新的POD_data:可预测的分支不会是一个重大的性能影响,并且如果用户连续多次使用它,则可以缓存POD_data&

如果POD_data中的内存足够大以至于它周围的内存是昂贵的,那么通过指针访问它的成本通常是适度的,而不必复制它的节省可能是显着的。