Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't a prvalue of array type be converted to the same type using static_cast?

#include <iostream>
int main(){
    using type = int[2];
    static_cast<type>(type{1,2});  //#1
}

Clang and GCC both complain that #1 is ill-formed, and give weird diagnoses.

Clang reports

static_cast from 'int *' to 'type' (aka 'int [2]') is not allowed  

GCC reports

invalid 'static_cast' from type 'type' {aka 'int [2]'} to type 'type' {aka 'int [2]'}   

However, as per expr.static.cast#4

An expression E can be explicitly converted to a type T if there is an implicit conversion sequence ([over.best.ics]) from E to T

Isn't that converts a type to the same type be called identity conversion?
over.best.ics#general-8

If no conversions are required to match an argument to a parameter type, the implicit conversion sequence is the standard conversion sequence consisting of the identity conversion ([over.ics.scs]).

I think a crucial rule here is

The sequence of conversions is an implicit conversion as defined in [conv], which means it is governed by the rules for initialization of an object or reference by a single expression ([dcl.init], [dcl.init.ref]).

That means, assume a result object would be t which will be initialized by the prvalue.

type t = type{1,2};  // #2

#2 is also rejected by Clang and GCC. However, such a case should be caught by dcl.init.general#15.9

Otherwise, the initial value of the object being initialized is the (possibly converted) value of the initializer expression. A standard conversion sequence ([conv]) will be used, if necessary, to convert the initializer expression to the cv-unqualified version of the destination type; no user-defined conversions are considered. If the conversion cannot be done, the initialization is ill-formed. When initializing a bit-field with a value that it cannot represent, the resulting value of the bit-field is implementation-defined.

According to basic.lval#1.2

A prvalue is an expression whose evaluation initializes an object or computes the value of an operand of an operator, as specified by the context in which it appears, or an expression that has type cv void.

In which case, Isn't that a prvalue of type type cannot initialize the result object?

Clang assumes that array-to-pointer conversion applies to the operand. However, such a conversion is only permitted if the bullet steps into expr.static.cast#8. In other words, the conversion should not apply here for the operand in bullet 4.

GCC gives a more nonreasonable diagnosis. I wonder why the explicit conversion is forbidden by Clang and GCC?

like image 509
xmh0511 Avatar asked Apr 09 '21 07:04

xmh0511


People also ask

What happens when static_cast fails?

If static_cast fails you will get a compile error and the program executable will never even be built. Your example has undefined behavior, not a failure or an error.

What does static_cast mean in C++?

The static_cast operator converts variable j to type float . This allows the compiler to generate a division with an answer of type float . All static_cast operators resolve at compile time and do not remove any const or volatile modifiers.

How is static_cast implemented?

static_cast is always resolved using compile-time type info. (This may involve a runtime action). If it's not an appropriate cast you either get a compile error or undefined behaviour. In your snippet it is OK because b is a D ; however if b were new B() then the cast compiles but causes undefined behaviour if run.

How do you convert a string from static to int?

One effective way to convert a string object into a numeral int is to use the stoi() function. This method is commonly used for newer versions of C++, with is being introduced with C++11. It takes as input a string value and returns as output the integer version of it.


1 Answers

Raw arrays are difficult to keep in array form - in prvalue context an array decays to a pointer.

But does it apply static_cast context also? The rules are outlined in [expr.static.cast]...

We're not casting to a reference, so we skip the first 3 clauses and arrive at [expr.static.cast]/4:

An expression E can be explicitly converted to a type T if there is an implicit conversion sequence ([over.best.ics]) from E to T ...

This won't work because ([conv.general]/3):

An expression E can be implicitly converted to a type T if and only if the declaration
T t=E; is well-formed, for some invented temporary variable t

And int t[2] = int[2]{1, 2}; isn't well-formed. The only legal way to initialize an array is specified in [dcl.init.general]/15.5:

... if the destination type is an array, the object is initialized as follows. Let x1, …, xk be the elements of the expression-list. ... the ith array element is copy-initialized with xi for each 1 ≤ ik.

There is no provision for "unwrapping" int[2]{1, 2} into an expression-list here.

Otherwise, the result object is direct-initialized from E.

Similarly this won't work for the same reason that int x[2](int[2]{1, 2}); isn't well-formed.

Note that there is a special provision with regard to arrays in aggregates, which allows for easy copying (e.g. std::array). There is no such treatment in raw arrays.

We skip over 5. 6, 7 don't apply. Which brings us to [expr.static.cast]/8:

The lvalue-to-rvalue ([conv.lval]), array-to-pointer ([conv.array]), and function-to-pointer ([conv.func]) conversions are applied to the operand.
. . .

So that's it, the cast is invalid. Both Clang and GCC seem to be correct. The error reporting differs slightly because Clang reports the decayed type, and GCC reports the original type.

As a workaround, cast to a reference type: static_cast<type&&>(type{1,2});.

like image 130
rustyx Avatar answered Oct 17 '22 13:10

rustyx