Constructor-Style Casting in Function Call Parameters

Constructor-style casting in function call parameters

[C++11: 5.2.3/1]: A simple-type-specifier (7.1.6.2) or typename-specifier (14.6) followed by a parenthesized expression-list constructs a value of the specified type given the expression list. [..]

Examining the grammar, we see that the only way to get unsigned char from the simple-type-specifier production is by concatenating two of them.

As evidence debunking the rumour that table 10 is stating the contrary, which I may myself have started a short while ago (:P), the table heading says "specifier(s)" (note the optional plural), and refer to the below passage:

[C++11: 5.2.3/2]: [..] Table 10 summarizes the valid combinations of simple-type-specifiers and the types they specify. (emphasis mine)

Now, combining simple-type-specifiers is allowed in some cases:

[C++11: 7.1.6.2/3]: When multiple simple-type-specifiers are allowed, they can be freely intermixed with other decl-specifiers in any order. [..]

… but there's no indication that this is the case with functional notation, which clearly states "a simple-type-specifier" — singular.

Therefore GCC is correct, and Visual Studio is wrong.

As for why this is the case... well, I don't know. I suspect we could come up with some ambiguous edge case, but Casey makes a good point in the comments below that allowing this would be inconsistent with function call syntax, since names of functions cannot have spaces in them.

Function style casting vs calling constructor

It's a functional-style type conversion which creates a t from an int by calling the constructor. There is no way to explicitly call a constructor in C++.

This is described in [expr.type.conv] (N3337):

5.2.3 Explicit type conversion (functional notation)

1) A simple-type-specifer (7.1.6.2) or typename-specifer (14.6) followed by a parenthesized expression-list
constructs a value of the specified type given the expression list. If the expression list is a single expression,
the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding
cast expression (5.4). If the type specified is a class type, the class type shall be complete. If the expression
list specifies more than a single value, the type shall be a class with a suitably declared constructor (8.5, 12.1),
and the expression T(x1, x2, ...) is equivalent in effect to the declaration T t(x1, x2, ...); for some invented temporary variable t, with the result being the value of t as a prvalue.

Since t is a simple-type-specifier, this is equivalent to the corresponding cast expression. This is allowed to carry out the equivalent of a static_cast ([expr.cast]/4), which defines the final result of the conversion:

[expr.static.cast]/4: Otherwise, an expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration T t(e); is well-formed, for some invented temporary variable t (8.5). The
effect of such an explicit conversion is the same as performing the declaration and initialization and then
using the temporary variable as the result of the conversion. The expression e is used as a glvalue if and
only if the initialization uses it as a glvalue.

Function-style cast vs. constructor

Syntactically, it is always a cast. That cast may happen to call a constructor:

char s [] = "Hello";
// Function-style cast; internally calls std::basic_string<char>::basic_string(char const*, Allocator)
std::string s2 = std::string(s);
// C-style cast; internally calls std::basic_string<char>::basic_string(char const*, Allocator)
std::string s3 = (std::string) s;

What exactly is or was the purpose of C++ function-style casts?

Function style casts bring consistency to primitive and user defined types. This is very useful when defining templates. For example, take this very silly example:

template<typename T, typename U>
T silly_cast(U const &u) {
return T(u);
}

My silly_cast function will work for primitive types, because it's a function-style cast. It will also work for user defined types, so long as class T has a single argument constructor that takes a U or U const &.

template<typename T, typename U>
T silly_cast(U const &u) {
return T(u);
}

class Foo {};
class Bar {
public:
Bar(Foo const&) {};
};

int main() {
long lg = 1L;
Foo f;
int v = silly_cast<int>(lg);
Bar b = silly_cast<Bar>(f);
}

Function style casting using the `new T()` operator in C++

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.

When do casts call the constructor of the new type?

Any time a new object is created, a constructor is called. A static_cast always results in a new, temporary object (but see comment by James McNellis) either
immediately, or through a call to a user defined conversion. (But in
order to have an object of the desired type to return, the user defined
conversion operator will have to call a constructor.)

When the target is a class type, C style casts and functional style
casts with a single argument are, by definition, the same as a
static_cast. If the functional style cast has zero or more than one
argument, then it will call the constructor immediately; user defined
conversion operators are not considered in this case. (And one could
question the choice of calling this a "type conversion".)

For the record, a case where a user defined conversion operator might be
called:

class A
{
int m_value;
public
A( int initialValue ) : m_value( initialValue ) {}
};

class B
{
int m_value;
public:
B( int initialValue ) : m_value( initialValue ) {}
operator A() const { return A( m_value ); }
};

void f( A const& arg );

B someB;
f( static_cast<A>( arg ) );

In this particular case, the cast is unnecessary, and the conversion
will be made implicitly in its absence. But in all cases: implicit
conversion, static_cast, C style cast ((A) someB) or functional
style cast (A( someB )),
B::operator A() will be called.)

C++ explicit constructor and cast

The parameter, w, of function doSomeWork is a Widget that you have created as a parameter in the line

doSomeWork(Widget(15));

doSomeWork expected a Widget and one has been explicitly supplied using the constructor you have listed. No compiler supplied copy constructor is used because the doSomeWork(const Widget &w) signature uses pass by reference semantics.

Just one object is constructed, although pass-by-value could have been used and clever modern compilers would defer the construction to the method's scope to avoid duplicated construction.

Constructing `long double` rvalue

Firstly, the original code is incorrect and it's a MSVC bug to silently accept it, see this question for standard references.


Since powl is a non-overloaded function of signature:

long double powl(long double x, long double y);

you can just write powl(4, 3), there is implicit coversion from integer to long double.

In the more general case your options include (long double)4, static_cast<long double>(4), 4.L, and 4.0L. The L suffix combined with the presence of . in the literal means a long double literal.


This feels dirty: I'm constructing an object, not casting an int, even if the compiler will elide it.

Maybe you have a misunderstanding in this area. The code double(4) is a cast; its official name is explicit type conversion (functional notation). Casts are prvalues, except for some cases that are not relevant here.

There is literally (ha ha) no difference whatsoever in semantics between the different syntaxes that result in prvalue of type double and value 4.

This is also true for class types; (std::string)x, std::string(x), and static_cast<std::string>(x) are all identical in semantics.



Related Topics



Leave a reply



Submit