25

Why does the following code not compile under g++ (C++14), MSVC (C++14), or ARM (C++03)?

The named Error instance calls the integer constructor, but the anonymous Error instance does not resolve.

class Error
{
public:
    Error(int err) : code_(err) {}
    const int code_;
};

enum Value
{
    value_1
};

int main()
{
    // compiles
    Error e(value_1);

    // does not compile under G++, ARM, or MSVC
    Error(value_1);
}

Example error under G++: (Coliru link)

g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out

main.cpp: In function 'int main()':
main.cpp:19:18: error: no matching function for call to 'Error::Error()'
     Error(value_1);
                  ^
main.cpp:4:5: note: candidate: Error::Error(int)
     Error(int err) : code_(err) {}
     ^~~~~
main.cpp:4:5: note:   candidate expects 1 argument, 0 provided
main.cpp:1:7: note: candidate: constexpr Error::Error(const Error&)
 class Error
       ^~~~~
main.cpp:1:7: note:   candidate expects 1 argument, 0 provided
main.cpp:1:7: note: candidate: constexpr Error::Error(Error&&)
main.cpp:1:7: note:   candidate expects 1 argument, 0 provided
Jim V
  • 1,131
  • 12
  • 22
  • 1
    Edited title: there is no **cast** in this code; the question is about a **conversion**. – Pete Becker Dec 01 '17 at 15:38
  • 1
    Possible duplicate of [Which part of the C++ standard allow to declare variable in parenthesis?](https://stackoverflow.com/questions/45991094/which-part-of-the-c-standard-allow-to-declare-variable-in-parenthesis) – underscore_d Dec 01 '17 at 15:44
  • Without the both of you specifying which compiler it did/didn't work on, the report isn't interesting. It may be an extension, but I didn't analyse the grammar yet. – underscore_d Dec 01 '17 at 15:46
  • 1
    @DevSolar `Value::value_1` is non-standard as this is not a `class enum`. –  Dec 01 '17 at 15:47
  • 2
    Use `Error{value_1};` – DaBler Dec 01 '17 at 17:21
  • 4
    @Ivan It *is* standard. Qualified enumerators are allowed even for old-style enums; see the C++11 standard [dcl.enum](10). – besc Dec 01 '17 at 17:41

3 Answers3

40

This comes from the same place as "The Most Vexing Parse" - the rule that if it can be a declaration, it is a declaration.
And surprisingly, you're allowed to put parentheses around the identifier in a variable declaration.
(I have no idea why, but I'm guessing that it simplified C's parser back in the day.)

The following are all valid declarations of int variables:

int (foo);
int (bar) = 0;
int (baz)(3);
int (twaddle)(baz);
molbdnilo
  • 64,751
  • 3
  • 43
  • 82
  • 7
    Why parentheses are allowed is explained in K&R. –  Dec 01 '17 at 15:51
  • 6
    [WP:Most vexing parse](https://en.wikipedia.org/wiki/Most_vexing_parse) – DevSolar Dec 01 '17 at 16:02
  • @Ivan Do you have a link/citation for the specific part of K&R in question? – JAB Dec 01 '17 at 18:11
  • 1
    @JAB No, but it the part that explains how type definitions work. Mentioned in my answer. –  Dec 01 '17 at 18:14
  • 4
    In some declarations, like a function pointer `int* (*f)();`, you need the parentheses to keep the stars associated with the right name. So they are allowed. There are just no rules saying when you must *not* use them, so they can be added whenever you like. – Bo Persson Dec 01 '17 at 22:42
  • And that's one reason for *uniform initialization*. Don't use parens here anymore! – JDługosz Dec 04 '17 at 10:10
37

The problem is that code

Error(value_1);

is a declaration of a variable value_1 of type Error.

This is a legacy from C language that uses expressions as part of type declaration.

For example int *i is a pointer to int because it says that expression *i should evaluate to type int. More examples of this:

  • int (*func)() is a pointer to function returning int because expression (*func)() evaluates to type int.
  • int *p[8] is an array of pointers to int because expression *p[x] evaluates to type int.
  • int (*p)[8] is a pointer to array of 8 int's (int[8]) because expression (*p)[x] evaluates to type int.
  • int (*(*p[8])())() is an array of 8 pointers to functions returning pointers to a function returning int because expression (*(*p[x])())() evaluates to type int.

Similarly int (i) is a plain variable of type int as expression (i) evaluates to type int.

And so because C++ inherits this from C, it uses parenthesis as part of type declaration, but also adds more syntax on top, leading to some unexpected results.

The rule applied by C++ here says to treat everything that can be a declaration as a declaration.


Similar confusion if often caused by code like this:

Error ec();

which is a forward declaration of a function ec that returns Error.

14

main.cpp:19:18: error: no matching function for call to 'Error::Error()'
Error(value_1);

The compiler tries to call the non-existent default constructor Error::Error() because it sees

Error(value_1);

as a variable declaration

Error  value_1;

A declaration is allowed to have redundant parenthesis.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203