3

This answer to What is The Rule of Three? has the following code. Notice that all constructors except the first one have = default; at the end of them:

class person
{
    std::string name;
    int age;

public:
    person(const std::string& name, int age);        // Ctor
    person(const person &) = default;                // 1/5: Copy Ctor
    person(person &&) noexcept = default;            // 4/5: Move Ctor
    person& operator=(const person &) = default;     // 2/5: Copy Assignment
    person& operator=(person &&) noexcept = default; // 5/5: Move Assignment
    ~person() noexcept = default;                    // 3/5: Dtor
};

Though I've seen it many times before, I don't understand when one might use = default, or why. It seems to me if you want the default constructor or assignment operator provided by C++, you can instead just remove that declaration (and any associated definition), no?

Is the purpose to explicitly forbid any other constructor of a given type? Ex: perhaps this

person& operator=(const person &) = default;     // 2/5: Copy Assignment

sitting at the top of a class definition just makes it crystal clear to any reader or user of the class that no other manual/explicit copy assignment operator exists or can exist?

I could use some help with my understanding here:

  1. the mechanics / what all does it (ie: using = default after a constructor or function definition in a class) work on?
    1. Would it work on the first constructor too? I don't see it on that one in the example: person(const std::string& name, int age); (also: does this constructor have a name? "general constructor" maybe?
  2. when to use
  3. why to use it

I'm still fairly new to the more advanced features of C++.

Update

Here is some additional insight from Scott Meyers: "A Concern about the Rule of Zero" (emphasis added):
(I'd probably make this an answer if this question wasn't closed).

The addition of the destructor has the side effect of disabling generation of the move functions, but because Widget is copyable, all the code that used to generate moves will now generate copies. In other words, adding a destructor to the class has caused presumably-efficient moves to be silently replaced with presumably-less-efficient copies. That strikes me as the kind of thing that (1) is likely to surprise people and (2) could really complicate debugging.

I'm inclined to recommend that a better way to rely on the compiler-generated copy and move functions is to expressly say that they do the right thing--to define them via =default:

 class Widget {
 public:
   Widget(const Widget&) = default;
   Widget(Widget&&) = default;

   Widget& operator=(const Widget&) = default;
   Widget& operator=(Widget&&) = default;

   ...
 };

With this approach, the spirit of the Rule of Zero remains: classes that don't manage resources should be designed so that the compiler-generated functions for copying, moving, and destruction do the right things. But instead of expressing this by not declaring those functions, it's expressed by declaring them explicitly and equally explicitly opting in to the compiler-generated implementations.

What do you think? Does this advice make sense? Should the Rule of Zero perhaps be the Rule of the Five defaults?

That is the end of Scott's blog post. Then, in the comments beneath his post, he states:

I think "The Rule of All or Nothing" would be a pretty good rule.


Response to being marked as a duplicate / request to re-open this question:

I feel like the question marked as "already has an answer" here doesn't cover the nuances of the question I'm asking, and my question should be reopened to allow others to contribute more answers and insight regarding the specifics of my question. I've looked at its answers. It's question focuses on explicitly defaulting a generic constructor, but my question focuses on explicitly defaulting one or more of the "Rule of 5" constructors/assignment operators, and how that affects the interactions and responses of the others. I'm voting to re-open my question now.

Sandbox: https://godbolt.org/z/vWMsd3

See also

  1. http://scottmeyers.blogspot.com/2014/03/a-concern-about-rule-of-zero.html
  2. What is The Rule of Three?
  3. The new syntax "= default" in C++11
  4. How is "=default" different from "{}" for default constructor and destructor?
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
  • 1
    The default constructor takes no arguments (so the one in the example can't be default) and initializes built-in types to zero (or to the value provided in the brace-or-equal-initializer) and calls the parameterless constructor for user-defined types. – Skusku Sep 11 '20 at 23:09
  • "It seems to me if you want the default constructor or assignment operator provided by C++, you can instead just remove that declaration (and any associated definition), no?" No, that's not _quite_ accurate. – Mooing Duck Sep 11 '20 at 23:19
  • I feel like the question marked as ["already has an anwer" here](https://stackoverflow.com/questions/20828907/the-new-syntax-default-in-c11) doesn't cover the nuances of the question I'm asking, and my question should be reopened to gain more answers and insight. – Gabriel Staples Sep 11 '20 at 23:27
  • Again, I'm voting to re-open this question because [the Q claimed to be a duplicate](https://stackoverflow.com/questions/20828907/the-new-syntax-default-in-c11) really only addresses the use of `=default` on the constructor. That's it! My Q digs deeper to ask about all 5 cases where it could be used. – Gabriel Staples Aug 15 '21 at 20:17

1 Answers1

7

Take a look at this table from Howard Hinnant showing when a special method is implicitly defaulted or not declared:

enter image description here

Yeaahh.. as you can see the rules are very complicated and trying to figure out yourself is a special member is implicitly defaulted or not declared is simply not an option if you have some special members user declared.

So the rule of thumb is if you declare at least one of them then you should default all of the rest.

bolov
  • 72,283
  • 15
  • 145
  • 224
  • 1
    Scott Meyers also made a case for always defaulting: http://scottmeyers.blogspot.com/2014/03/a-concern-about-rule-of-zero.html – user4581301 Sep 11 '20 at 23:16
  • `So the rule of thumb is if you declare at least one of them then you should default all of the rest`...OR explicitly define them, presumably? ie: do `= default;` to the end of each of the other 4 of the 5, if you manually define 1, OR manually define them as well? – Gabriel Staples Sep 11 '20 at 23:35
  • @bolov, what's the source for that table? – Gabriel Staples Sep 11 '20 at 23:37
  • @bolov, can you explain the table a bit more? I'm struggling to see and understand which sections apply when I do `person(const person &) = default;` vs nothing, `person& operator=(const person &) = default;` vs nothing, etc. – Gabriel Staples Sep 11 '20 at 23:41
  • @GabrielStaples how to read the table: e.g.: look at the 4th row. It tells you that: if the destructor is user declared then: the def ctor is implicitly defaulted, the cpy ctor and cpy assign is is implicitly defaulted and the move ctor and move assign is not declared. – bolov Sep 12 '20 at 00:08
  • @GabrielStaples default all of them means: `special_method(...) = default;` – bolov Sep 12 '20 at 00:09
  • 1
    @GabrielStaples : [Howard Hinnant's presentation on Move semantics](https://www.youtube.com/watch?v=vLinb2fgkHk) has a good discussion on special member functions. – user4581301 Sep 12 '20 at 02:02
  • @bolov, you forgot to post the source for the table, so I did some digging. it looks like Howard Hinnant is the source for the table: https://howardhinnant.github.io/classdecl.html --> scroll to bottom. He then describes this table in this video presentation of his, starting at 13:57, here: [Engineering Distinguished Speaker Series: Howard Hinnant, 13:57](https://youtu.be/vLinb2fgkHk?t=837). – Gabriel Staples Sep 13 '20 at 01:32
  • Yeah I should have mentioned that was the first place I saw the chart. I meant to, but forgot. – user4581301 Sep 13 '20 at 03:17