1) New expression
A new-expression is a special case of... a new-expression. Nothing less, nothing more. It results in creation of object of given type in dynamic storage and , for class-types, in a call to constructor with given argument list. For trivial types new-expression provides an initializer.
A new-expression isn't a cast, albeit initializing parameters from argument list given in parenthesis may involve implicit casts. If returned result of new-expression wasn't used, the created object would continue to exist. And unless there is a curious contingency involved, it would not be correctly freed.
A functional cast would produce an xvalue, an object which would expire at end of enclosing expression.
C++ parser by design is a "greedy" parser. It doesn't try to single out every token first, it's a complex algorithm to match statements to whole patterns, as longer as possible, appropriate for current context (and context may change in result). The "greedy" nature becomes obvious in case of some ill-formed or ambiguous code. As a rule, if something can be parsed wrong, it will be parsed wrong, but not until whole statement would be analyzed.
Speaking of functional cast, typename(name)
syntax may appear in declarations of variables or types, which result in following being a legal declaration:
int foo ( int (a) )
{
return a;
}
It's because since times of C we can declare some eldritch array of pointers as void (*(*f[])())()
, which declares f
as array of pointers to function returning a pointer to function returning void.
In result, initializing a variable with a functional cast may create an ambiguous code:
float fvar = 1.0f;
double dvar ( int(fvar) );
It declares a double(int)
function named dvar
! You wouldn't spot it, until you try assign to it:
auto v = dvar; // v is a pointer to function.
dvar = 4; // error
The declaration of dvar as whole matches BOTH a function declaration and a variable declaration. In this case compiler MUST choose wrongly, but diagnostics are optional. It's exacerbated by fact that C and C++ are allowing variable identifiers to match type names.
2) Initialization of object
Yes, the expression test(1)
is a functional cast even if test is a class. By definition it results in considering an existing constructor to be called.
class test {
public:
test(int arg): b(arg) {}
protected:
int b;
};
Here constructor test(int)
is called a conversion constructor as it can take exactly one parameter and its usage permits use of type-id test
in functional cast - from int
or int
-compatible type. Constructors with more than one parameter without default value do not bear this name. For class test
, te expression test(1,2)
is ill-formed.
In fact, unless such constructor qualified as explicit
, it would allow an implicit conversion:
struct test {
test(int a) {std::cout << "int " << a << std::endl;}
explicit test (float a) {std::cout << "float " << a << std::endl;}
};
int main()
{
test b = 1; // calls (int) constructor
test c = 2.0f; // calls (int) constructor
test d = test(3.0f); // function cast calls (float) constructor
}
P.S. C++11 allowed to escape functional cast syntax for initialization of class-type object. For above class it would be test{1}
. But it also can be an aggregate initialization, if test
would be trivial:
struct test {
int b;
};
test t = test{1};