0

When I was reading about the most vexing parse in C++, I got even more confused about the different interpretations of expression of the form a_type_name(args) (args is optional). There are at least the following many use cases:

  1. Create an unnamed temporary of type a_type_name. For example:

    Widget w = Widget("a simple widget");  // explicitly calling Widget constructor taking a string
    f = processWidget(Widget());  // pass a default constructed Widget to function processWidget
    

    I never truly understand why this form of expression is valid, except just taking it as is. After all, can you do something like int() to define (or declare?) an unnamed integer? Or double(3.14) to initialize an unnamed double? I can't find in my book the C++ Primer any formal definition of this kind of expression --- Widget() where the declarator is missing. Can any one point me to a formal explanation?

  2. An unnamed function pointer

    As explained in the most vexing parse, when Widget() could declare a unnamed function (pointer?) that takes nothing and returns a Widget. If this is valid, how does C++ type deduce the following expression:

    auto w = Widget(); // is w of type Widget Or a function pointer where the function takes no parameter and returns a Widget?
    

    Can someone list all the context in which expression of the form Widget() means a function (pointer)?

  3. Functional form of the C-style cast

    If there is a conversion path, either through converting constructors or conversion operators or some other predefined implicit conversion rules, expression of the form a_type_name(one_arg) can mean an explicit type conversion:

    Widget("convert string to widget") // a form of explicit cast
    

There may be other interpretations that I haven't encountered in some other contexts. Please help me disambiguate all these use cases.

Community
  • 1
  • 1
Rich
  • 1,669
  • 2
  • 19
  • 32
  • These are all the same case. The expression `Type(stuff)` means: create object of type `Type` with initializers `stuff`. – M.M Sep 24 '15 at 04:19

2 Answers2

1
A a(10);

Can never be a problem. It will always be on object of type A, constructed with argument 10.

A a(int);

Can never be a problem. It will always be a function that takes an int and return an A.

A a();

is subject to the most vexing parse.

Extend that to some thing more complex.

A a(B b(), C c());

is subject to the most vexing parse.

A a(B(10), c(30.2f));

is not subject to the most vexing parse.

A a(B b(int), C c(float));

is not subject to the the most vexing parse.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

On point 1. Assuming Widget is a struct or class,

f = processWidget( Widget() );

For that to work, processWidget must be declared something like:

int processWidget( const & Widget );

Though it can be slightly complicated by the fact that there may be classes derived from Widget that can automatically convert to a Widget, or other contexts in which such a conversion is provided, let's just deal with this basic form. Widget() is going to create a widget "on the spot". In other contexts we might have written

Widget w();

Which is exactly the same thing, except now the created widget has a name (w). Widget() is going to create a temporary, which will be destroyed immediately after the statement in which it appears is finished. As such, this temporary Widget is provided as a const & Widget to the function processWidget. It will last as long as that function executes. When that function returns, it will provide the return value to f, at which point the temporary Widget will be destroyed (it's destructor function will be called).

Note, too, that you can do:

Widget w;
w = Widget();

This odd usage constructs a temporary widget, which is copied to w. The reason this is applicable to your question about processWidget( Widget() ) is that = in this context is an operator. It could be an overloaded operator function written to provide some unique copy operation, but even the automatically generated version of it takes a parameter, the const & Widget I mentioned for the parameter in the declaration of processWidget. That means both are basically the same thing.

To that extent, int() does something similar, but that exact form would fashion an int uninitialized in some compilers, which isn't good (in modern C++ it would be zero initialized).

However, if Widget() is a function returning a value, declared like:

int Widget();

Then, the processWidget would have to be declared in such a way to accept an integer, or a type that converts to an integer, in order for the call to work.

As to point 2:

Widget() would only be "seen" as a function type if there is a function declaration. If Widget is a struct or class, the compiler determines that by lookup. When the compiler attempts to understand what is meant by the use of Widget, followed by the recognizable function operator (), it then "sees" there was a declaration for a function of that name, which then gives it knowledge of the return type, parameters declared, etc. In other words, Widget() as an expression does not have an isolated meaning to C++, being not a keyword or other recognizable declaration, and is given context from which meaning is deduced based on the declaration provided. If Widget were declared as a struct, the compiler would not "know" Widget to be a function declaration, but may understand it to be a struct for which either an automatic default constructor was provided (or will be provided), or for which one suitably matching declaration was given.

As to point 3.

Constructors are understood to be a unique category of functions by C++. Any constructor taking a single argument implies that the parameter provided is a way of constructing the object from other given types. In your question, the comment you provided with

Widget("convert string to widget") // a form of explicit cast

Suggests this is a cast, but it's not. It's a conversion. There is a huge difference. A cast doesn't create a new object, but a conversion does.

Constructors taking one parameter are assumed to be conversion declarations, they specify how to create a Widget given some other type. If a Widget can be constructed from a const char *, as you've hinted, it means one constructor in the Widget class is declare as:

Widget( const char * );

This means you can make a Widget out of a string literal, or something that converts to a string literal (const char *). In contexts where a Widget might be required as a parameter, this tells the compiler it could construct one if a const char * is provided. This is the exact opposite of conversion operators, where they return a representation of Widget as if it were some other type. If, for example, you could represent a Widget as a std::string, you might provide a conversion operator for one (though, such operators can be tricky). A rather typical usage is to provide a conversion operator to bool. This doesn't mean one can represent Widget as a bool, but if a Widget type were used in the context OF a bool, say, if ( !w ), where w is of type Widget, then that Widget ACTS like a bool at that moment, probably indicating the Widget is not ok and something should be done.

JVene
  • 1,611
  • 10
  • 10
  • "A cast doesn't create a new object, but a conversion does." - this is incorrect. `T(x)` and `(T)x` are equivalent. The result of a cast to object type is a temporary object. – M.M Sep 24 '15 at 06:23
  • Isn't `Widget w();` an example of the vexing parse Rich is asking about? – Bo Persson Sep 24 '15 at 06:35