在副本和交换习语中实现交换

gre*_*iod 4 c++ copy-constructor assignment-operator copy-and-swap c++11

下面什么是复制和交换习惯用法以及如何为我的班级提供交换功能,我尝试实现交换功能,如后面接受的答案选项2(具有调用成员函数的自由函数)而不是直接友好前链接中的自由功能.

但是以下不编译

#include <iostream>

// Uncommenting the following two lines won't change the state of affairs
// class Bar;
// void swap(Bar &, Bar &);
class Bar {
public:
  Bar(unsigned int bottles=0) : bottles(bottles) { enforce(); } // (1)
  Bar(Bar const & b) : bottles(b.bottles) { enforce(); } // (1)

  Bar & operator=(Bar const & b) {
    // bottles = b.bottles;
    // enforce();
    // Copy and swap idiom (maybe overkill in this example)
    Bar tmp(b); // but apart from resource management it allows (1)
                // to enforce a constraint on the internal state
    swap(*this, tmp); // Can't see the swap non-member function (2)
    return *this;
  }

  void swap(Bar & that) {
    using std::swap;
    swap(bottles, that.bottles);
  }

  friend std::ostream & operator<<(std::ostream & out, Bar const & b) {
    out << b.bottles << " bottles";
    return out;
  }

private:
  unsigned int bottles;
  void enforce() { bottles /=2; bottles *= 2; } // (1) -- Ensure the number of bottles is even
};

void swap(Bar & man, Bar & woman) { // (2)
  man.swap(woman);
}

int main () {
  Bar man (5);
  Bar woman;

  std::cout << "Before -> m: " << man << " / w: " << woman << std::endl;
  swap(man, woman);
  std::cout << "After  -> m: " << man << " / w: " << woman << std::endl;

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我知道复制和交换习惯在这里是过度的,但它也允许通过复制构造函数(1)对内部状态强制执行一些约束(更具体的例子是以缩小形式维护一个分数).不幸的是,这不能编译,因为编译器看到的(2)的唯一候选者是Bar :: swap成员函数.我是否坚持使用朋友的非会员功能方法?

编辑:回到下面的答案看看我最后得到了什么感谢这个问题的所有答案和评论.

Ric*_*ges 5

我认为我们发布了c ++ 11?

在这种情况下,std :: swap的默认实现将是最优的,前提是我们正确实现了move-assignment操作符和move-constructor(理想情况下nothrow)

http://en.cppreference.com/w/cpp/algorithm/swap

#include <iostream>

class Bar {
public:
    Bar(unsigned int bottles=0) : bottles(bottles) { enforce(); } // (1)
    Bar(Bar const & b) : bottles(b.bottles) {
        // b has already been enforced. is enforce necessary here?
        enforce();
    } // (1)
    Bar(Bar&& b) noexcept
    : bottles(std::move(b.bottles))
    {
        // no need to enforce() because b will have already been enforced;
    }

    Bar& operator=(Bar&& b) noexcept
    {
        auto tmp = std::move(b);
        swap(tmp);
        return *this;
    }

    Bar & operator=(Bar const & b)
    {
        Bar tmp(b); // but apart from resource management it allows (1)
        swap(tmp);
        return *this;
    }

    void swap(Bar & that) noexcept {
        using std::swap;
        swap(bottles, that.bottles);
    }

    friend std::ostream & operator<<(std::ostream & out, Bar const & b) {
        out << b.bottles << " bottles";
        return out;
    }

private:
    unsigned int bottles;
    void enforce() {  } // (1)
};

/* not needed anymore
void swap(Bar & man, Bar & woman) { // (2)
    man.swap(woman);
}
*/
int main () {
    Bar man (5);
    Bar woman;

    std::cout << "Before -> m: " << man << " / w: " << woman << std::endl;
    using std::swap;
    swap(man, woman);
    std::cout << "After  -> m: " << man << " / w: " << woman << std::endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

预期结果:

Before -> m: 5 bottles / w: 0 bottles
After  -> m: 0 bottles / w: 5 bottles
Run Code Online (Sandbox Code Playgroud)

编辑:

为了任何关心表现的人(例如@JosephThompson)的利益,请允许我减轻您的顾虑.将调用移动std::swap到虚函数(强制clang生成任何代码)然后使用带有-O2的apple clang进行编译后,这:

void doit(Bar& l, Bar& r) override {
    std::swap(l, r);
}
Run Code Online (Sandbox Code Playgroud)

成了这个:

__ZN8swapper24doitER3BarS1_:            ## @_ZN8swapper24doitER3BarS1_
    .cfi_startproc
## BB#0:
    pushq   %rbp
Ltmp85:
    .cfi_def_cfa_offset 16
Ltmp86:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp87:
    .cfi_def_cfa_register %rbp
    movl    (%rsi), %eax
    movl    (%rdx), %ecx
    movl    %ecx, (%rsi)
    movl    %eax, (%rdx)
    popq    %rbp
    retq
    .cfi_endproc 
Run Code Online (Sandbox Code Playgroud)

看到?最佳.c ++标准库摇滚!