是CppCoreGuidelines C.21正确吗? [英] Is CppCoreGuidelines C.21 correct?

查看:224
本文介绍了是CppCoreGuidelines C.21正确吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在阅读Bjarne Stroustrup的CoreCppGuidelines时,我发现了一条与我的经验相矛盾的指南。





2。当编译器的隐式动作不正确时,始终定义或删除特殊成员。



3。不依赖于过时的行为(上图中的红框)。如果您声明任何析构函数,复制构造函数或复制赋值运算符,则声明复制构造函数和复制赋值运算符。



4 。 从不删除移动成员。如果你这样做,最好是多余的。在最坏的情况下,它会打破你的类(如上面的 SimpleValue 示例)。如果你删除了移动成员,这是多余的情况,那么你强迫你的读者不断审查你的班,以确保它不是破碎的情况。



5。给6个特殊成员的每一个投标爱护,即使结果是让编译器为你处理它(可能通过抑制或删除它们)。



6。将特殊成员按照一致的顺序放在课程顶部明确声明),以便您的读者不必去搜索它们。我有我最喜欢的订单,如果你喜欢的订单是不同的,罚款。我的首选顺序是我在 SimpleValue 示例中使用的顺序。


While reading the Bjarne Stroustrup's CoreCppGuidelines, I have found a guideline which contradicts my experience.

The C.21 requires the following:

If you define or =delete any default operation, define or =delete them all

With the following reason:

The semantics of the special functions are closely related, so if one needs to be non-default, the odds are that others need modification too.

From my experience, the two most common situations of redefinition of default operations are the following:

#1: Definition of virtual destructor with default body to allow inheritance:

class C1
{
...
    virtual ~C1() = default;
}

#2: Definition of default constructor making some initialization of RAII-typed members:

class C2
{
public:
    int a; float b; std::string c; std::unique_ptr<int> x;

    C2() : a(0), b(1), c("2"), x(std::make_unique<int>(5))
    {}
}

All other situations were rare in my experience.

What do you think of these examples? Are they exceptions of the C.21 rule or it's better to define all default operations here? Are there any other frequent exceptions?

解决方案

I have significant reservations with this guideline. Even knowing that it is a guideline, and not a rule, I still have reservations.

Let's say you have a user-written class similar to std::complex<double>, or std::chrono::seconds. It is just a value type. It doesn't own any resources, it is meant to be simple. Let's say it has a non-special-member constructor.

class SimpleValue
{
    int value_;
public:
    explicit SimpleValue(int value);
};

Well, I also want SimpleValue to be default constructible, and I've inhibited the default constructor by providing another constructor, so I need to add that special member:

class SimpleValue
{
    int value_;
public:
    SimpleValue();
    explicit SimpleValue(int value);
};

I fear that people will memorize this guideline and reason: Well, since I've provided one special member, I should define or delete the rest, so here goes...

class SimpleValue
{
    int value_;
public:
    ~SimpleValue() = default;
    SimpleValue();
    SimpleValue(const SimpleValue&) = default;
    SimpleValue& operator=(const SimpleValue&) = default;

    explicit SimpleValue(int value);
};

Hmm... I don't need move members, but I need to mindlessly follow what the wise ones have told me, so I'll just delete those:

class SimpleValue
{
    int value_;
public:
    ~SimpleValue() = default;
    SimpleValue();
    SimpleValue(const SimpleValue&) = default;
    SimpleValue& operator=(const SimpleValue&) = default;
    SimpleValue(SimpleValue&&) = delete;
    SimpleValue& operator=(SimpleValue&&) = delete;

    explicit SimpleValue(int value);
};

I fear CoreCppGuidelines C.21 will lead to a ton of code that looks just like this. Why is that bad? A couple of reasons:

1. This is far more difficult to read than this correct version:

class SimpleValue
{
    int value_;
public:
    SimpleValue();
    explicit SimpleValue(int value);
};

2. It is broken. You'll find out the first time you try to return a SimpleValue from a function by value:

SimpleValue
make_SimpleValue(int i)
{
    // do some computations with i
    SimpleValue x{i};
    // do some more computations
    return x;
}

This won't compile. The error message will say something about accessing a deleted member of SimpleValue.

I've got some better guidelines:

1. Know when the compiler is defaulting or deleting special members for you, and what defaulted members will do.

This chart can help with that:

If this chart is far too complex, I understand. It is complex. But when it is explained to you a little bit at a time it is much easier to deal with. I will hopefully be updating this answer within a week with a link to a video of me explaining this chart. Here is the link to the explanation, after a longer delay than I would have liked (my apologies): https://www.youtube.com/watch?v=vLinb2fgkHk

2. Always define or delete a special member when the compiler's implicit action is not correct.

3. Don't depend on deprecated behavior (the red boxes in the chart above). If you declare any of the destructor, copy constructor, or copy assignment operator, then declare both the copy constructor and the copy assignment operator.

4. Never delete the move members. If you do, at best it will be redundant. At worst it will break your class (as in the SimpleValue example above). If you do delete the move members, and it is the redundant case, then you force your readers to constantly review your class to make sure it is not the broken case.

5. Give tender loving care to each of the 6 special members, even if the result is to let the compiler handle it for you (perhaps by inhibiting or deleting them implicitly).

6. Put your special members in a consistent order at the top of your class (only those you want to declare explicitly) so that your readers don't have to go searching for them. I've got my favorite order, if your preferred order is different, fine. My preferred order is that which I used in the SimpleValue example.

这篇关于是CppCoreGuidelines C.21正确吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆