Casts that look like function calls?

  • Thread starter dpbsmith.janissary.2006
  • Start date
D

dpbsmith.janissary.2006

I know C++ mostly from "learning by doing." My main reference is
Stoustrup's book. I was puzzled by something in a colleague's code that
looked like this:

abc=BOOL(def)

I asked him what that was, and he said "it's a cast." I know all about
dynamic_cast and friends, but this was something new to me. To make a
long story short, both of my colleagues seemed to be familiar with
writing a cast as if it were a function, although they don't normally
write them that way, and Stoustrup's book has actual instances of this
that appear in some of his code examples. A colleague was able to find
something in the formal C++ syntax description indicating it's valid...
but nobody was able to point me to any documentation, description, or
explanation.

If this is described in Stroustrup's book, I couldn't find it. I
thought "deprecated C-style cast" might be it, but, no, that's just the
regular C cast syntax, and I've verified that C compilers do _not_
accept writing a cast as if it were a function.

It is not a Microsoft-ism, either, which was my other thought; other
C++ compilers seem to accept it.

So, what the heck is it? Is it fully legitimate C++? Does it have any
differences whatsoever from a "normal" cast, i.e. do

hij = long(klm);
and
hij = (long) klm;

compile identical code? If so, what is it there for? Is it just an
unintended consequence of things that needed to be done to extend C
syntax to C++? And where exactly in Stoustrup's book is it described
and explained?
 
R

ralpe

I know C++ mostly from "learning by doing." My main reference is
Stoustrup's book. I was puzzled by something in a colleague's code that
looked like this:

abc=BOOL(def)

This is normal constructor syntax. Assuming that BOOL is a type and not
a function name the above line constructs a variable of type BOOL from
def, which is not the same as a cast from def to BOOL. If BOOL is a
class or struct the code will only compile if BOOL has a constructor
that takes an argument of def's type. If BOOL is a typedef for a
primitive type the code will compile if def is convertable to that
type. Only in the latter case is the above line comparable to a cast.

Ralpe
 
?

=?iso-8859-1?q?Ernesto_Basc=F3n?=

ralpe ha escrito:

To me, it is defined as a macro that does the simple cast and should be
defined as:

#define BOOL(x) (bool) x

as you can see, everytime the BOOL(x) macro is called, the preprocessor
will transform it onto (bool) x.

I do not see the macro usage useful and it makes the code very hard to
read and understand. The
 
M

Marcus Kwok

ralpe said:
This is normal constructor syntax. Assuming that BOOL is a type and not
a function name the above line constructs a variable of type BOOL from
def, which is not the same as a cast from def to BOOL. If BOOL is a
class or struct the code will only compile if BOOL has a constructor
that takes an argument of def's type. If BOOL is a typedef for a
primitive type the code will compile if def is convertable to that
type. Only in the latter case is the above line comparable to a cast.

Also, the function-style cast will not work for types that have a space
in them (e.g., "unsigned long").
 
?

=?iso-8859-1?q?Erik_Wikstr=F6m?=

I know C++ mostly from "learning by doing." My main reference is
Stoustrup's book. I was puzzled by something in a colleague's code that
looked like this:

abc=BOOL(def)

I would suspect that this is actually a macro since there is no
BOOL-type in C++. BOOL is probably a macro looking something like this:

#define BOOL(x) x > 0 ? 1 : 0;
I asked him what that was, and he said "it's a cast." I know all about
dynamic_cast and friends, but this was something new to me. To make a
long story short, both of my colleagues seemed to be familiar with
writing a cast as if it were a function, although they don't normally
write them that way, and Stoustrup's book has actual instances of this
that appear in some of his code examples. A colleague was able to find
something in the formal C++ syntax description indicating it's valid...
but nobody was able to point me to any documentation, description, or
explanation.

If this is described in Stroustrup's book, I couldn't find it. I
thought "deprecated C-style cast" might be it, but, no, that's just the
regular C cast syntax, and I've verified that C compilers do _not_
accept writing a cast as if it were a function.

It is not a Microsoft-ism, either, which was my other thought; other
C++ compilers seem to accept it.

So, what the heck is it? Is it fully legitimate C++? Does it have any
differences whatsoever from a "normal" cast, i.e. do

hij = long(klm);
and
hij = (long) klm;

My compiler takes both of them without any problem. A guess would be
that there somewhere in the standard says that there should exist
functions with the same name as the basic types that returns a value of
that type (so you can use it kind of like a constructor) which might be
what the second is.
 
M

Marcus Kwok

And where exactly in Stoustrup's book is it described
and explained?

I found it in Section 6.2.8 - Constructors (p.131 in the Special
Edition).

The T(e) construct is sometimes referred to as a function-style cast.
Unfortunately, for a built-in type T, T(e) is equivalent to (T)e
(section 6.2.7).
 
N

Noah Roberts

ralpe said:
This is normal constructor syntax. Assuming that BOOL is a type and not
a function name the above line constructs a variable of type BOOL from
def, which is not the same as a cast from def to BOOL.

Not true. It is, in fact, a cast. It has exactly the same meaning as
(BOOL)def. Many people, myself included for a long time, assume that
such "constructor calls" are just that. For instance:

std::string x = std::string("Hello ") + "World!";

The call "std::string("Hello")" is a _cast_, not a constructor call or
initialization. It resolves to the same thing as:

std::string x = (std::string)"Hello " + "World";

or

std::string x = static_cast<std::string>("Hello ") + "World";

Now all these casts eventually resolve to a constructor invocation
because in order to cast to that type an instance of that type must be
constructed. However, these are in fact casts and as a form of C-Style
cast they have the same weakness (can resolve to a reinterpret without
warning).

Do a search in this group for discussions about "constructor calls" and
whether or not such a thing even exists. Many argue that there is no
way for the programmer to directly "call" the constructor. The
standard calls all things that look like constructor calls either
"function style cast" or "initialization".

Even though our in our coding standard we don't allow C-style casts (I
finally convinced everyone this was necissary) we do allow such
function style casts in cases such as "string construction".

<quote>
Function style casts have the same problems as c-style casts and can be
used in place of them. They are allowed in one case only; that is when
they look like a constructor call:

extern const char * str; std::string s = std::string("Hello") +
str;

It is often assumed that the case "std::string("Hello")" is a
constructor call but it is actually a cast. When you could normally
construct an object with a parameter of that type (using "new
TYPE(x)"), a function style cast is allowed. Under no other condition
will you use one.
</quote>
 
N

Noah Roberts

Marcus said:
I found it in Section 6.2.8 - Constructors (p.131 in the Special
Edition).

The T(e) construct is sometimes referred to as a function-style cast.
Unfortunately, for a built-in type T, T(e) is equivalent to (T)e
(section 6.2.7).

Stroustrup seems to deviate from the standard here then. I have to
admit that I have never read his book.

5.2.3 Explicit type conversion (functional notation)

1 A simple type specifier (7.1.5) 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).

Sumarizing the rest: if it is a class type it must be complete. I the
parameter list is more than one expression it is equivelent to creating
and initializing a temporary rvalue of type T.
 
M

Marcus Kwok

Noah Roberts said:
Marcus said:
I found it in Section 6.2.8 - Constructors (p.131 in the Special
Edition).
[quote moved below]

Stroustrup seems to deviate from the standard here then. I have to
admit that I have never read his book.

Hmm, maybe this can be reconciled by the part that I didn't quote.
5.2.3 Explicit type conversion (functional notation)

1 A simple type specifier (7.1.5) followed by a parenthesized
expression-list constructs a value of the specified type given the
expression list.

Before the other passage I quoted, he also states:

The construction of a value of type T from a value e can be expressed
by the functional notation T(e).

and then gives an example of usage.

[quote from the Standard]
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).

[original quote from Stroustrup]
With that addition, I don't see how it deviates from the passage from
the Standard, though I have to admit that I haven't read the entire
Standard in depth, nor am I an expert in Standardese.
 
R

Ron Natalie

ralpe said:
This is normal constructor syntax.

It isn't a constructor syntax at all. It's an explicit conversion
(function style) according to the specification. The word BOOL
here need not be the name of a constructor. It can be any type
name, even a typedef.
>Only in the latter case is the above line comparable to a cast.
Completely and totally incorrect. Any time a function-style
conversion is used with exactly one argument it is EXACTLY
equivelent BY DEFININTION to the cast style conversion.
 
R

Ron Natalie

Marcus said:
I found it in Section 6.2.8 - Constructors (p.131 in the Special
Edition).

The T(e) construct is sometimes referred to as a function-style cast.
Unfortunately, for a built-in type T, T(e) is equivalent to (T)e
(section 6.2.7).
For any type.
 
D

dpbsmith

Summing up: here's what I think I've gleaned. (Oh, I forgot to explain:
BOOL is a Microsoft-ism, but it is not a macro that takes arguments,
it's just typedef int BOOL;)

Suppose we have an explicit class, rather than a built-in type, like

const int five = 5;
class foo {
public:
foo(int x);
}

1) Then foo(five) is _not_ a "call to a constructor function;" it is,
semantically, a _cast_ of an "int" to a "foo". However, performing the
cast involves invoking the constructor. Sounds like Humpty-Dumpty
language to me, but OK.

2) Therefore, in C++, the syntax C(e), where C is a programmer-defined
class, is a cast, not a function call.

3) It sounds to me like an _unintended side effect,_ that occurred in
order to avoid complicating the syntax, that T(e), where T is a
built-in type like "long" or "float," is also a cast.

4) It is _completely equivalent to_ (generates the same code as) C
casting syntax.

5) I can't quite make out what Stoustrup is saying when he says
"Pointer conversions cannot be expressed using the T(e) notation. For
example, char*(2) is a syntax error. Unfortunately, the protection that
the constructor notation provides against such dangerous conversions
can be circumvented by using typedef names." This sounds as if he first
makes a recommendation to use the T(e) notation on the basis of safety,
then noting that it doesn't actually provide safety.

6) If it is not an unintended side-effect, then it seems to be
motivated by some need for it in templates.

7) It does not seem as if there's any particular reason to prefer this
notation, or use it at all, except when writing templates. It would
seem to me that anyone doing C-style casts _of pointers_ would have to
be aware of what they are doing. It's not the case that habitually
using these constructor-style casts would give you protection against
mental lapses or anything like that?
 
R

Ron Natalie

1) Then foo(five) is _not_ a "call to a constructor function;" it is,
semantically, a _cast_ of an "int" to a "foo". However, performing the
cast involves invoking the constructor.

It's never a call to the constructor, even in the case of other than
exactly one argument. You can't call constructors directly,
constructors don't have return value, etc... It's a creation
of a temporary object which involves storage allocation, and
a few other things in addition to the invocation of the constructor.
2) Therefore, in C++, the syntax C(e), where C is a programmer-defined
class, is a cast, not a function call.

It's never a function call when C is a type name.
3) It sounds to me like an _unintended side effect,_ that occurred in
order to avoid complicating the syntax, that T(e), where T is a
built-in type like "long" or "float," is also a cast.

Huh? It's always a cast. It's never a function call.
 
A

Alf P. Steinbach

* (e-mail address removed):
Summing up: here's what I think I've gleaned. (Oh, I forgot to explain:
BOOL is a Microsoft-ism, but it is not a macro that takes arguments,
it's just typedef int BOOL;)

Suppose we have an explicit class, rather than a built-in type, like

const int five = 5;
class foo {
public:
foo(int x);
}

1) Then foo(five) is _not_ a "call to a constructor function;" it is,
semantically, a _cast_ of an "int" to a "foo". However, performing the
cast involves invoking the constructor. Sounds like Humpty-Dumpty
language to me, but OK.

It is /syntactically/ a cast.

A cast is a syntax concept, what you write. A conversion is a semantic
concept, what happens. For example

int x;
double y = 4.56;
x = y; // This is a conversion, but it's not a cast.

What happens for foo(five) depends on the type that foo is.

While we're on the topic of terminology, I see that Ron has jumped in
criticizing your use of "call constructor".

The standard and most everyone else use your terminology.

2) Therefore, in C++, the syntax C(e), where C is a programmer-defined
class, is a cast, not a function call.

Almost right. The "therefore" is meaningless.

3) It sounds to me like an _unintended side effect,_ that occurred in
order to avoid complicating the syntax, that T(e), where T is a
built-in type like "long" or "float," is also a cast.

It's not clear why it turned out this way. But it's all bound up with
the (1) the idea of converting constructors, and (2) the idea that
terseness, having things done implicitly for you, is extremely valuable.
At least one of those was perhaps not such a good idea.

4) It is _completely equivalent to_ (generates the same code as) C
casting syntax.
Yes.


5) I can't quite make out what Stoustrup is saying when he says
"Pointer conversions cannot be expressed using the T(e) notation. For
example, char*(2) is a syntax error. Unfortunately, the protection that
the constructor notation provides against such dangerous conversions
can be circumvented by using typedef names." This sounds as if he first
makes a recommendation to use the T(e) notation on the basis of safety,
then noting that it doesn't actually provide safety.

T(e) requires T to be a "simple type-specifier", which is a possibly
namespace- or class- qualified class, enum, typedef or built-in type name.

6) If it is not an unintended side-effect, then it seems to be
motivated by some need for it in templates.

No, the basic syntax is older than templates.

7) It does not seem as if there's any particular reason to prefer this
notation, or use it at all, except when writing templates. It would
seem to me that anyone doing C-style casts _of pointers_ would have to
be aware of what they are doing. It's not the case that habitually
using these constructor-style casts would give you protection against
mental lapses or anything like that?

The T(e) notation does not protect you against anything.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,731
Messages
2,569,432
Members
44,832
Latest member
GlennSmall

Latest Threads

Top