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.