【问题标题】:Custom swap boilerplate自定义交换样板
【发布时间】:2026-02-01 22:30:01
【问题描述】:

据我了解复制和交换习语,它具有需要样板代码的缺点。考虑一个简单的“just-hold-all-those-damn-lemons”结构:

struct MuchData {
private:
    std::string one, two;
    int three;
    std::vector<Something> four;
    MyType five;
    MyOtherType six, seven;
    unsigned long long int still_overflows;

public:
    MuchData() : one("."), two("/"), three(0), four(), five(), six(-1), seven(0), still_overflows(0) 
    { }

    MuchData(const MuchData& rhs) : one(rhs.one), two(rhs.two), three(rhs.three), four(rhs.four), five(rhs.five), six(rhs.six), seven(rhs.seven), still_overflows(rhs.still_overflows) 
    { }

    MuchData(MushData&& old) : one("."), two("/"), three(0), four(), five(), six(-1), seven(0), still_overflows(0)
    { swap(*this, old); }

    MuchData& operator=(MuchData old) { swap(*old, this); return *this; }

    friend void swap(MuchData& left, MushData&right) {
        using std::swap;

        swap(left.one, right.one);
        swap(left.two, right.two);
        swap(left.three, right.three);
        swap(left.four, right.four);
        swap(left.five, right.five);
        swap(left.six, right.six);
        swap(left.seven, right.seven);
        swap(left.still_overflows, right.still_overflows);
    }

    // And now we can go and do something interesting
};

使用用

编写的初始化程序
: one(".")
, two("/")
, three(0)
// etc.

style,这段代码占用了更多空间。而MyTypeMyOtherType 也可能是用这种技术定义的……有什么办法可以减少这里的重复性吗?例如,当添加新字段时,很容易忘记添加相应的swap(...) 行,从而导致神秘的切片。

【问题讨论】:

    标签: c++ boilerplate


    【解决方案1】:

    您可以做的一件事是将成员存储在 std::tuple 中并为它们提供命名访问器,如下所示:

    struct MuchData {
    private:
      std::tuple<std::string, std::string, int, std::vector<Something>, MyType, MyOtherType, unisgned long long> data;
    
      std::string& one() { return std::get<0>(data); }
      const std::string& one() const { return std::get<0>(data); }
    
      //etc.
    };
    

    是的,您正在用一个样板交换另一个样板,但您只会写一次名称;例如,构造函数的语法会更加简洁。即使您忘记添加访问器,swap() 也可以正常工作(因为它只是 swap() 元组)。

    使用 C++1y,您甚至可以使用 auto&amp;const auto&amp; 作为访问器的返回类型,从而消除更多重复。

    【讨论】:

    • 不错。由于 MSVC 2010(我使用的)或多或少地提供了对 std::tuple 的体面支持,这就是我将使用一段时间的答案。
    【解决方案2】:

    您可以使用类内初始化程序来减少将字段初始化为其标准值的构造函数之间的重复:

    class MuchData {
      std::string one{"."};
      std::string two{"/"};
      // ...
    

    那么默认和移动构造函数就变得微不足道了。

    MuchData() = default;
    
    MuchData(MuchData&& o) { swap(*this, o); }
    

    你可以默认复制构造函数:

      MuchData(const MuchData&) = default;
    

    【讨论】:

    • 好答案。不幸的是,我在相当长的一段时间内都无法离开 MSVC 2010 ——但总有一天会使用这个答案。