0

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?

Dan
  • 2,694
  • 1
  • 6
  • 19
  • 1
    (1) `int* b = new int(a);` - create an `int` on the free-store, initialises it with the value of `a` and returns it's address which is assigned to `b` . For (2) please give a [mcve]; the answer will probably involve one of the forms of initialisation [Initialization](https://en.cppreference.com/w/cpp/language/initialization) but which will depend on how `test` is declared. – Richard Critten Jun 21 '22 at 14:27
  • 1
    1) No. 2) `test t = test(1);` is copy initialization where the subexpression `test(1)` is a function style cast which will use the appropriate constructor. – Jason Jun 21 '22 at 14:27
  • Ad 1) no, this is [new expression](https://en.cppreference.com/w/cpp/language/new). Ad 2) comma separated expressions is an expression as well. – freakish Jun 21 '22 at 14:29
  • 2
    Note function style casting is not recommended in C++ (it is more of a "C" leftover), read more on casting here : [explicit type conversions and static cast](https://www.learncpp.com/cpp-tutorial/explicit-type-conversion-casting-and-static-cast/) – Pepijn Kramer Jun 21 '22 at 14:30
  • @user17732522 @freakish your answers for (2) is contradicting, so `T(x,y)` is an expression and still counts as a cast? – Dan Jun 21 '22 at 14:31
  • No (2) are two expressions in the parentheses. `1, 2` is not an expression in this context, it is an _expression-list_. I don't know what @freakish means. The standard technically lists the case of multiple expressions in the expression list also under the heading "_Explicit type conversion (functional notation)_", but it only actually is equivalent to a C-style explicit cast if there is a single expression in it. – user17732522 Jun 21 '22 at 14:36
  • @user17732522 thanks, in what context `1, 2` would count as a single expression? – Dan Jun 21 '22 at 14:38
  • @Dan Everywhere where it cannot be an expression list, e.g. `int x = (1, 2);`. Here `1, 2` is a single expression using the comma operator. – user17732522 Jun 21 '22 at 14:40
  • @user17732522 hmm thank you, but that made things worse. What does that expression even mean? never seen it before. – Dan Jun 21 '22 at 14:46
  • @Dan The built-in comma operator evaluates the left-hand side, then the right-hand side and results in the right-hand side's result. But `operator,` can be overloaded. It is probably the most obscure operator to overload and you almost never see it. In this case `1, 2` is just equivalent to `2`. – user17732522 Jun 21 '22 at 14:58
  • @user17732522 i see, so its just the result (or side effect?) of how the parser is designed. I also tried it in C and it equals to `2`. – Dan Jun 21 '22 at 15:00
  • No, it's a result of how the _language_ is designed (and it means the same thing in C as it does in C++). – Paul Sanders Jun 21 '22 at 15:16
  • Right, so the grammar is designed to accept that syntax, I read more on it here: https://stackoverflow.com/questions/52550/what-does-the-comma-operator-do – Dan Jun 21 '22 at 15:23
  • @PepijnKramer function-style cast and C-style cast are separate things. Function style cast is result of C++ syntax to construct a new object as an xvalue. It requires compatible types or proper argument to match a constructor. C-style cast is contending with `reinterpret_cast` – Swift - Friday Pie Jun 21 '22 at 15:23
  • @Swift-FridayPie A functional style cast (with exactly one parenthesized expression) is exactly equivalent to a C-style cast. For example `using A = int*; using B = float*; auto x = A(B());` compiles, while `using A = int*; using B = float*; auto x = A{B()};` doesn't. – user17732522 Jun 21 '22 at 15:36
  • It's more useful to think of `int(a)` as a constructor call. Same as `test(1)` is a constructor call. This even works with new expressions `new int(5)` calls the `int` constructor with argument `5` for some heap allocated storage. So just ignore that `int` is a fundamental type and not a class and all makes sense. – Goswin von Brederlow Jun 21 '22 at 15:46
  • @Swift-FridayPie Even though I've written functions like that, I never knew they had the "function style cast" name. But yes those casts are nice explicit and type safe so no problem :) Thanks for showing me – Pepijn Kramer Jun 21 '22 at 16:05

3 Answers3

1

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.

Jason
  • 36,170
  • 5
  • 26
  • 60
1

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};
Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
-1

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, ...

And if we check out the page on direct initialization, we find that syntax (3) on that page is

T ( other )
T ( arg1, arg2, ... )
  1. 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.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
  • The cpp standard defines comma separated expressions to be an expression as well. And in fact it even states that the type of comma separated expressions is the type of the right most expression. From that point of view it makes sense to cast comma separated expression. – freakish Jun 21 '22 at 14:36
  • 1
    "_and direct initialization is defined to perform a functional cast when necessary_": That is wrong. The page is listing the situations under which direct-initialization is performed. A functional cast can result in direct-initialization, not the other way around. A functional cast would e.g. allow to initialize a `int*` from a `double*`, which a `new` expression doesn't allow. – user17732522 Jun 21 '22 at 14:48