Here is an example of a simple function style casting done by int(a)
:
float a = 5.0;
int b = int(a);
More info from cpprefrenece:
The functional cast expression consists of a simple type specifier or a typedef specifier followed by a single expression in parentheses.
I have 2 questions:
1) would using the new
operator still count as a functional cast?
int* b = new int(a);
2) Assuming test
is a class, then test t = test(1);
is a function style casting to test
but test t = test(1, 2);
isn't because it has more than 1 expression in parenthesis?
CodePudding user response:
would using the new operator still count as a functional cast?
No, the use of the new
operator(like you used in your example) is not a use case of functional cast.
test t = test(1, 2);
isn't because it has more than 1 expression in parenthesis?
Both test t = test(1);
and test t = test(1,2);
are copy initializations. Now, the subexpression test(1)
is indeed a functional cast where the appropriate test
constructor will be used. While the subexpression test(1, 2)
is not as it has more than a single expression inside parenthesis.
CodePudding user response:
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};
CodePudding user response:
New Operator
Yes. From the cppreference page on the new
keyword
The object created by a new-expression is initialized according to the following rules:
- For non-array type, ...
- If initializer is a parenthesized list of arguments, the object is direct-initialized.
And if we check out the page on direct initialization, we find that syntax (3) on that page is
T ( other ) T ( arg1, arg2, ... )
- initialization ... by functional cast or with a parenthesized expression list
So new
is defined to perform direct initialization, and direct initialization is defined to perform a functional cast when necessary. There's more going on with regard to allocation in the background, but at some point down the line a functional cast may get performed.
More than 1 Expression
In regards to your second question, a "cast" is a conversion from one type to another. An initialization with one argument can be viewed as a cast, from the argument type T
to the result type S
. It makes no sense to think of test(1, 2)
as a conversion from anything, since it takes two arguments and produces one value.