227

Is there any good reason that an empty set of round brackets (parentheses) isn't valid for calling the default constructor in C++?

MyObject  object;  // ok - default ctor
MyObject  object(blah); // ok

MyObject  object();  // error

I seem to type "()" automatically everytime. Is there a good reason this isn't allowed?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
Martin Beckett
  • 94,801
  • 28
  • 188
  • 263
  • Someone should come up with a better title for this, but I can't think of what that would be. At least spell out "constructor" to help the search engine(s). – Adam Mitz Oct 08 '08 at 05:18
  • 1
    And this is just another good example where C++ is context sensitive. The example code in the question would also fail if `blah` would be a class. – Albert Aug 27 '10 at 21:03
  • 1
    One thing that I noticed is that if I only have the default constructor then the compiler doesn't give any error if I use `()` e.g. `MyObject object` works as usual & `MyObject object()` does not give any error! Could someone please explain why? I mean I haven't defined the function in my `main`... so it should give an error, right? Thanks in advance! – Milan Nov 12 '20 at 00:04

9 Answers9

191

Most vexing parse

This is related to what is known as "C++'s most vexing parse". Basically, anything that can be interpreted by the compiler as a function declaration will be interpreted as a function declaration.

Another instance of the same problem:

std::ifstream ifs("file.txt");
std::vector<T> v(std::istream_iterator<T>(ifs), std::istream_iterator<T>());

v is interpreted as a declaration of function with 2 parameters.

The workaround is to add another pair of parentheses:

std::vector<T> v((std::istream_iterator<T>(ifs)), std::istream_iterator<T>());

Or, if you have C++11 and list-initialization (also known as uniform initialization) available:

std::vector<T> v{std::istream_iterator<T>{ifs}, std::istream_iterator<T>{}};

With this, there is no way it could be interpreted as a function declaration.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Constantin
  • 27,478
  • 10
  • 60
  • 79
  • I had read Effective STL but I didn't remember seeing that. i will have to read it again, thanks – Martin Beckett Oct 08 '08 at 16:19
  • 7
    Nitpick: you _can_ declare functions inside functions. It's called _local functions_ in C, and at least `extern "C" foo();`-style is also allowed in C++. – Marc Mutz - mmutz Aug 08 '09 at 10:20
  • Thanks, mmutz, don't know what i was thinking when i wrote it, probably confused declaration with definition. Edited the answer accordingly. – Constantin Aug 12 '09 at 17:56
  • 4
    How can that be interpreted as a function? – Casebash Oct 29 '10 at 01:00
  • 14
    @Casebash, `std::vector` is return type; `v` is function name; `(` opens formal argument list; `std::istream_iterator` is type of first argument; `ifs` is name of first argument, `()` around `ifs` are effectively ignored; second `std::istream_iterator` is type of second argument, which is unnamed, `()` around it are also ignored; ');' closes argument list and function declaration. – Constantin Oct 30 '10 at 07:31
  • 2
    There is an ambiguity in the grammar involving expression-statements and declarations: An expression-statement with a function-style explicit type conversion as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration. (C++ ISO/IEC (2003) 6.8.1) – bartolo-otrit Sep 28 '12 at 09:12
  • 12
    @Constantin, the parentheses after the second argument are not ignored. The second parameter is not a `std::istream_iterator` but a pointer/reference to a function that takes no arguments and returns an `istream_iterator`. – CTMacUser Feb 15 '14 at 00:25
  • This "feature" lets you declare functions without #including the function's header file. Yes, simple text inclusion could still blow an Ancient C compiler's resource limits if the header was large/complex enough. Remember that there was only 1 namespace for functions (which `main`, the Standard library, your OS's library, 3rd-party code, and your code all shared) so there was no ambiguity where the function came from. – CTMacUser Feb 15 '14 at 00:29
  • If it's not the *most* vexing parse, can we call it *a* vexing parse? It still is vexing, is it not? – Aaron McDaid Jun 26 '15 at 10:03
  • Do you need `std::istream_iterator{}` or is `std::istream_iterator` sufficient? – Post Self Apr 17 '17 at 14:49
  • @PostSelf `std::istream_iterator` is type name, you can't pass type to function, but you can infer type and call it's default constructor with just `{}` – OwnageIsMagic Nov 18 '20 at 17:04
123

Because it is treated as the declaration for a function:

int MyFunction(); // clearly a function
MyObject object(); // also a function declaration
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
1800 INFORMATION
  • 131,367
  • 29
  • 160
  • 239
  • 1
    But it should give an error, right? Because we haven't defined the `object()` function right? Could you please elaborate on that? I'm confused right now. Thank you so much in advance! – Milan Nov 12 '20 at 00:07
  • On a side note, in my `main`, I even tried these: `any_variable_name random_function_name()` e.g. `int func1()` , `double func2()`, `void func3()`, etc. and all of them works i.e. my program gets compiled without any error! However, I haven't defined any of those functions, so, I should get errors, right? – Milan Nov 12 '20 at 00:13
  • 1
    @Milan I would expect linker errors if you actually tried to call those functions. Otherwise they are just declarations – 1800 INFORMATION Nov 15 '20 at 22:31
52

The same syntax is used for function declaration - e.g. the function object, taking no parameters and returning MyObject

Nemanja Trifunovic
  • 24,346
  • 3
  • 50
  • 88
11

Because the compiler thinks it is a declaration of a function that takes no arguments and returns a MyObject instance.

Fred Larson
  • 60,987
  • 18
  • 112
  • 174
7

I guess, the compiler would not know if this statement:

MyObject object();

is a constructor call or a function prototype declaring a function named object with return type MyObject and no parameters.

Black
  • 5,022
  • 2
  • 22
  • 37
7

You could also use the more verbose way of construction:

MyObject object1 = MyObject();
MyObject object2 = MyObject(object1);

In C++0x this also allows for auto:

auto object1 = MyObject();
auto object2 = MyObject(object1);
dalle
  • 18,057
  • 5
  • 57
  • 81
  • 5
    This requires a copy constructor and is inefficient – Casebash Oct 29 '10 at 01:01
  • 9
    @Casebash: The compiler is probably smart enough to use some `RVO`-like optimization prevent it from being inefficient. – dalle Oct 29 '10 at 11:23
  • 2
    "Probably" means "I am guessing". Regarding optimization people usually do not want to guess but rather take the explicit way. – Stefan Nov 04 '14 at 14:35
  • 7
    @Stefan: You do not need to "guess"; copy elision will happen here in all mainstream compilers and that's been the case for well over a decade. Not that this is good code. – Lightness Races in Orbit Jun 25 '15 at 18:19
  • @Casebash since C++11 here will be used move assignment operator, which is faster for classes, holding resourses on heap. – yanpas May 04 '16 at 10:20
  • 1
    Since C++17 there is no copy/move, the behaviour is defined as identical to direct initialization – M.M Feb 22 '22 at 23:45
5

As mentioned many times, it's a declaration. It's that way for backward compatibility. One of the many areas of C++ that are goofy/inconsistent/painful/bogus because of its legacy.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
5

From n4296 [dcl.init]:

[ Note:
Since () is not permitted by the syntax for initializer, X a(); is not the declaration of an object of class X, but the declaration of a function taking no argument and returning an X. The form () is permitted in certain other initialization contexts (5.3.4, 5.2.3, 12.6.2).
—end note ]

C++11 Link
C++14 Link

R Sahu
  • 204,454
  • 14
  • 159
  • 270
Andreas DM
  • 10,685
  • 6
  • 35
  • 62
3

As the others said, it is a function declaration. Since C++11 you can use brace initialization if you need to see the empty something that explicitly tells you that a default constructor is used.

Jedi luke{}; //default constructor
Hitokage
  • 733
  • 8
  • 19