What is the Rule of Four (and a half)?

后端 未结 2 554
离开以前
离开以前 2020-12-29 07:52

For properly handling object copying, the rule of thumb is the Rule of Three. With C++11, move semantics are a thing, so instead it\'s the Rule of Five. However, in discussi

2条回答
  •  旧巷少年郎
    2020-12-29 08:39

    Are there any disadvantages or warnings for this approach, compared to the Rule of Five?

    Although it can save code duplication, using copy-and-swap simply results in worse classes, to be blunt. You are hurting your class' performance, including move assignment (if you use the unified assignment operator, which I'm also not a fan of), which should be very fast. In exchange, you get the strong exception guarantee, which seems nice at first. The thing is, that you can get the strong exception guarantee from any class with a simple generic function:

    template 
    void copy_and_swap(T& target, T source) {
        using std::swap;
        swap(target, std::move(source));
    }
    

    And that's it. So people who need strong exception safety can get it anyway. And frankly, strong exception safety is quite a niche anyhow.

    The real way to save code duplication is through the Rule of Zero: choose member variables so that you don't need to write any of the special functions. In real life, I'd say that 90+ % of the time I see special member functions, they could have easily been avoided. Even if your class does indeed have some kind of special logic required for a special member function, you are usually better off pushing it down into a member. Your logger class may need to flush a buffer in its destructor, but that's not a reason to write a destructor: write a small buffer class that handles the flushing and have that as a member of your logger. Loggers potentially have all kinds of other resources that can get handled automatically and you want to let the compiler automatically generate copy/move/destruct code.

    The thing about C++ is that automatic generation of special functions is all or nothing, per function. That is the copy constructor (e.g.) either gets generated automatically, taking into account all members, or you have to write (and worse, maintain) it all by hand. So it strongly pushes you to an approach of pushing things downwards.

    In cases where you are writing a class to manage a resource and need to deal with this, it should typically be: a) relatively small, and b) relatively generic/reusable. The former means that a bit of duplicated code isn't a big deal, and the latter means that you probably don't want to leave performance on the table.

    In sum I strongly discourage using copy and swap, and using unified assignment operators. Try to follow the Rule of Zero, if you can't, follow the Rule of Five. Write swap only if you can make it faster than the generic swap (which does 3 moves), but usually you needn't bother.

提交回复
热议问题