【问题标题】:Efficiently creating C++11 move and copy aware structs高效地创建 C++11 移动和复制感知结构
【发布时间】:2017-04-23 22:39:21
【问题描述】:

我正在尝试组合一个高效的 C++1x 结构,该结构可以利用高效的 std::move/copy 构造/和赋值操作。这些结构分为 2 个基本类别,POD 结构和非 POD 结构。我已经养成了使用样板代码编写这些结构的习惯,但我很确定编译器在这方面可以做得比我做得更好,而且每个类都需要大量输入。我的问题是利用默认编译器操作我能做的最低限度是什么。例如,我知道,一旦我定义了一个显式构造函数,它就会抑制移动和赋值运算符的自动生成。此外,我希望没有超类的非 POD 结构具有默认析构函数,而那些从基类继承的结构具有默认的虚拟析构函数。如何有效地执行这些操作的规则一直让我有点困惑。

PAGE_DATA 结构的原始版本如下

using PAGE_DATA = struct page_data {
    std::string name;
    std::vector<SCRN_TEXT> elements;
    std::vector<int> jumpTable;
};

然后我添加了样板代码以使该数据结构能够感知移动,并且我还添加了一个显式构造函数(没有它,我不得不使用读起来不太好的大括号通过聚合初始化来初始化它)。

using PAGE_DATA = struct page_data {
    explicit page_data(const std::string& rName = std::string(), 
        const std::vector<SCRN_TEXT>& rElements = std::vector<SCRN_TEXT>(), 
        const std::vector<int>& rJumpTable = std::vector<int>())
        : name(rName)
        , elements(rElements)
        , jumpTable(rJumpTable)
    {}
    //! Defaulted copy constructor.
    page_data(const page_data&) = default;
    //! Defaulted move constructor.
    page_data(page_data&&) = default;

    //! Non-throwing copy-and-swap idiom unified assignment.
    page_data& operator=(page_data rhs) {
        rhs.swap(*this);
        return *this;
    }

    //! Non-throwing-swap idiom.
    void swap(page_data& rhs) noexcept {
        // enable ADL (not necessary in our case, but good practice)
        using std::swap;
        // swap base members
        // ...
        // swap members here
        swap(name, rhs.name);
        swap(elements, rhs.elements);
        swap(jumpTable, rhs.jumpTable);
    }

    virtual ~page_data() = default;

    std::string name;
    std::vector<SCRN_TEXT> elements;
    std::vector<int> jumpTable;
}; 

调用的依赖简单POD结构如下图:

using COLOR_TYPE = enum color_type
{
    BLACK   = 0x0,
    CYAN    = 0x1,
    RED     = 0x2,
    YELLOW  = 0x3,
    GREEN   = 0x4,
    MAGENTA = 0x5,
    AMBER   = 0x6,
    WHITE   = 0x7
};

using SIZE_TYPE = enum size_type {
    SMALL_CHAR = 0x0,
    BIG_CHAR = 0x1
};

using MODIFY_TYPE = enum modify_type {
    NORMAL      = 0x0,
    UNDER       = 0x2,
    FLASH       = 0x4,
    FLASH_UNDER = 0x6
};

using SCREEN_ATTR = struct screen_attr {
    COLOR_TYPE    color  : 4;
    SIZE_TYPE     size   : 2;
    MODIFY_TYPE   blink  : 4;
    unsigned char unused : 6;
};

using CDU_ROWCOL = struct cdu_rowcol {
    int  v;
    int  h;
};

using SCRN_TEXT = struct scrn_text {
    CDU_ROWCOL loc;
    SCREEN_ATTR  attrib;
    std::string text;
};

在整理了live coliru demo 之后,我能够验证样板做的事情是正确的。

int main() {
    std::cout << "assertions work fine" << std::endl;
    static_assert(std::is_copy_constructible<PAGE_DATA>(), "not copy constructible");
    static_assert(std::is_move_constructible<PAGE_DATA>(), "not move constructible");
}

【问题讨论】:

  • 你为什么要坚持在每个结构声明前加上using ALL_CAPS =?这不是你typedef struct 的东西。
  • @NicolBolas 这是 C++11 的别名(现代 typedef)表示法。 Caps 是别名,小写是 struct,这允许我同时使用 Typedef 和 Struct,小写的用于定义构造函数签名。
  • 我知道using 是什么意思。我不知道你为什么认为这样做很重要,甚至是个好主意。就像我说的,这不是 C。在 C 中,struct 单独不会创建类型名称;在 C++ 中,确实如此。 screen_attr 是一个完全有效的类型名称。你不需要 typedef/使用SCREEN_ATTR

标签: c++11 struct c++14 move defaulted-functions


【解决方案1】:

我的问题是,要利用默认的编译器操作,我能做的最低限度是什么。

最低限度(相当理想的数量)是您的原始版本,它充分利用了默认操作。

您的page_data::swap 可能比通用的std::swap 更有效,并且它不会阻止任何隐式成员函数的生成,因此可能值得保留。但是,如果有的话,它可能只会稍微好一点,因此您可能想要衡量是否值得弄乱您的类定义。

【讨论】:

    【解决方案2】:

    你原来的结构已经完全可以移动建造了。您编写的所有额外样板代码都已在需要时由编译器生成。

    【讨论】:

    • 问题是我需要添加一个显式的默认构造函数来避免大括号初始化。我有这些 PAGE_DATA 结构的向量。我不得不 emplace_back({..}, {..}, {..}) 这些或使用聚合初始化器创建本地 PAGE_DATA 结构。如果我有非 POD 简单结构的继承,也会发生什么。我不确定在我可能有 struct Foo : public Bar 的情况下会发生什么,以及如何确保 foo 使用 Bar 等做所有正确的事情,
    • @johnco3: "问题是我需要添加一个显式的默认构造函数来避免大括号初始化。" ...你为什么想要我>?您将所有成员设为public;为什么要防止人们对其使用聚合初始化?
    • 仅添加默认构造函数比实现复制/移动操作更糟糕?
    【解决方案3】:

    我喜欢this 对我的question 的回答,“...如果您发现自己在编写移动运算符和移动构造函数,那是因为您没有充分分解问题。”

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-18
      • 2014-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-31
      • 1970-01-01
      相关资源
      最近更新 更多