1

Context

According to the tutorial, if a class defines a default constructor Rectangle();, it'sinvoked via:

int main () {
    Rectangle recta; // valid - default constructor
    Rectangle rectb(); // valid - but not a default constructor
    Rectangle rectc{}; // valid - default constructor
}

It is said that the second call is a "function declaration", so its interpreted by the compiler as some function that returns an object of type Rectangle. Yet in the subsequent sections of that same tutorial, it refers to Rectangle(); as a default constructor.

Question(s)

So this is a two part question:

  1. Why are default constructors defined with () if calling with () does not even invoke them?
  2. Why does the use of {} invoke the default constructor if defined with ()?

Thoughts/Examples

W.R.T the first item, it seems contradictory that the default constructor definition must be either:

  • Completely omitted, or
  • Defined with empty parenthesis

Yet calling with empty parenthesis does not invoke the default constructor. The best that I can reason is that the compiler interprets the call to rectb() as some kind of "inline" function definition that returns an object of type Rectangle; because of this, if the default constructor had member assignment in it, this newly returned object would not have any members assigned and would all be NULL (or so I believe).

Now to expand the confusion with a detailed example:

#include <iostream>
using namespace std;

class Circle
{
public:
    Circle(); // M1
    Circle(float r); // M2
};

Circle::Circle()
{
    cout << "default constructor" << endl;
}

Circle::Circle(float r)
{
    cout << "custom constructor" << endl;
}

int main() {
    Circle c1(1.0); // calls M2
    Circle c2(); // does not call M1
    return 0;
}

// Outputs
"custom constructor"
0

So if the compiler interprets a line such as Circle c2(); as a newly declared function returning type Circle, why does it not do that for Circle c1(1.0); and is instead able to find a matching constructor?

W.R.T. the second item, I believe this is the result of a C++11 design implementation that enables to compiler to handle value-initialization from what I've read. Since it uses {} and not () the compiler does not parse this as a function declaration.

References

Now while I am researching this, the other questions I have found include:

  1. Default constructor with empty brackets

Expands on something called the "most vexing parse" in C++, surrounding how Circle c2(); is treated as a function declaration as noted. However, the question (#1) I am asking expands on this, namely why are what appear to be two function declarations treated differently despite having definitions within the class?

  1. C++ default Constructor not being called

Marked as a duplicate of the previous question, but with examples aligned to this post.

  • 2
    "Most vexing parse" is the keyword you're looking for. – Silvio Mayolo Nov 25 '21 at 17:37
  • 1
    Look up "most vexing parse" - put simply if a statement could be parsed as a function definition then it is a function definition. See also https://en.wikipedia.org/wiki/Most_vexing_parse – Richard Critten Nov 25 '21 at 17:38
  • @RichardCritten I keep stumbling upon this, and have come to that conclusion. My confusion remains in why the "most vexing parse" applies to `Circle c2();` and not `Circle c1(1.0);` in my example. One is interpreted as a function declaration, the other calls a constructor, but why the difference? –  Nov 25 '21 at 17:41
  • `Circle c1(1.0);` can't be a function declaration, so it's not treated as one. – cigien Nov 25 '21 at 17:42
  • 1
    @madeslurpy the 2nd one cannot be (is not) a valid function declaration. A function declaration would look like `Circe c1(double)` Please note the type in the parenthesis as oposed to a value. – bolov Nov 25 '21 at 17:42
  • @bolov Thank you, I come from a Python background and haven't used C++ for much of anything in years. This implementation detail was confusing me greatly, and to know its as simple as that is frustrating yet relieving! –  Nov 25 '21 at 17:44
  • @RichardCritten -- if you look carefully at the page you linked to, you'll see that `Rectangle rectb();` is **not** an example of the most vexing parse. That refers to a more complex and more obscure issue. `Rectangle rectb();` is simply a function declaration. Yes, both come from the same rule, but that's not what makes it "vexing". – Pete Becker Nov 25 '21 at 17:47
  • @PeteBecker Not sure I follow, admittedly in the tutorial its actually `Rectangle rectc();` but what's the different between just a function declaration and the most vexing parse? –  Nov 25 '21 at 17:55
  • 1
    @madeslurpy, Syntactically there _is no difference._ That's what makes it "vexing:" You have to know the special rule that applies in this special case. Modern C++ is the result of almost fifty years of gradual evolution. Not everything about it makes sense if you don't take that whole long history into account. – Solomon Slow Nov 25 '21 at 17:58
  • @madeslurpy -- if you tell a beginner that `int f();` is a function declaration, they'll understand it. If you tell a beginner that `TimeKeeper time_keeper(Timer());` is a function declaration they'll be vexed. (that's the canonical example from [Wikipedia](https://en.wikipedia.org/wiki/Most_vexing_parse_)). It's partly a matter of degree, and partly about familiarity. – Pete Becker Nov 25 '21 at 18:13
  • *"Why are default constructors defined with () if calling with () does not even invoke them?"* -- ah, but calling with () does invoke them --- but pay attention to the details! You are wondering why `rectb()` does not invoke `Rectangle()`. Notice the difference? The constructor `Rectangle()` can be invoked via `Rectangle()`, as in `Rectangle rectb = Rectangle();`. The problem comes when you try to invoke `Rectangle()` using *only* the parentheses. The syntax `Rectangle rectb();` requires interpretation -- to what do the parentheses belong? To the distant `Rectangle` or the closer `rectb`? – JaMiT Nov 25 '21 at 19:35

0 Answers0