Would a static_cast be better style here?

S

Steven T. Hatton

The code shown below is an example from the Coin3D documentation. I believe
the use of the C-style cast is safe under the circumstances, but from what
I've been exposed to (TC++PL(SE)), I would favor using a static_cast. Is
there any technical reason to favor the C-style over a static_cast?

http://doc.coin3d.org/Coin/index.html

void foo(SoNode * node)
{
if (node->getTypeId() == SoFile::getClassTypeId()) {
SoFile * filenode = (SoFile *)node; // safe downward cast, knows the
type
}
else if (node->getTypeId().isOfType(SoGroup::getClassTypeId())) {
SoGroup * group = (SoGroup *)node; // safe downward cast, knows the
type
}
}
 
I

Ioannis Vranos

Steven T. Hatton said:
The code shown below is an example from the Coin3D documentation. I believe
the use of the C-style cast is safe under the circumstances, but from what
I've been exposed to (TC++PL(SE)), I would favor using a static_cast. Is
there any technical reason to favor the C-style over a static_cast?


No, if you need a cast, use only the new C++ ones (static_cast,
dynamic_cast, reinterprer_cast). They are there for a reason, to help you
identify potential problems. For example if your code crashes, the first
thing you will do is check the reinterpret_casts at first.






Ioannis Vranos
 
N

Nils Petter Vaskinn

No, if you need a cast, use only the new C++ ones (static_cast,
dynamic_cast, reinterprer_cast).

One advantage beeing that you can much more easily search for
static_cast<something>(variable) than (something) variable.
 
P

Peter Koch Larsen

Steven T. Hatton said:
The code shown below is an example from the Coin3D documentation. I believe
the use of the C-style cast is safe under the circumstances, but from what
I've been exposed to (TC++PL(SE)), I would favor using a static_cast. Is
there any technical reason to favor the C-style over a static_cast?

None whatsoever. Actually, I would advice you never ever use the C-style
cast. The only purpose of it is to be compatible with C.

/Peter
 
J

Jerry Coffin

Steven T. Hatton said:
The code shown below is an example from the Coin3D documentation. I believe
the use of the C-style cast is safe under the circumstances, but from what
I've been exposed to (TC++PL(SE)), I would favor using a static_cast. Is
there any technical reason to favor the C-style over a static_cast?

Given the situation, you _probably_ want to use a dynamic_cast instead
of either one. Generally this is a type of situation you want to
avoid though -- except in a few situations like re-creating an object
that's been persisted to a stream of some sort, downcasting tends to
indicate poor design.

Downcasting gives you direct access to the functionality of a derived
class. Generally speaking, the preferred method of doing this is to
wrap that functionality in a virtual functio in the base class so you
can use it without casting down to the derived type.

Contrary to statements elsethread, a C-style cast can do one thing
none of the new casts can. It has nothing to do with the question at
hand, so I won't go into it, but it does exist.
Later,
Jerry.
 
I

Ioannis Vranos

Jerry Coffin said:
"Steven T. Hatton" <[email protected]> wrote in message

Given the situation, you _probably_ want to use a dynamic_cast instead
of either one. Generally this is a type of situation you want to
avoid though -- except in a few situations like re-creating an object
that's been persisted to a stream of some sort, downcasting tends to
indicate poor design.

Downcasting gives you direct access to the functionality of a derived
class. Generally speaking, the preferred method of doing this is to
wrap that functionality in a virtual functio in the base class so you
can use it without casting down to the derived type.


For downcasts static_cast is better to be used. dynami_cast is for upcasting
and crosscasting.



Contrary to statements elsethread, a C-style cast can do one thing
none of the new casts can. It has nothing to do with the question at
hand, so I won't go into it, but it does exist.


Don't tease our curiosity. Tell it. :)






Ioannis Vranos
 
K

Kevin Goodsell

Ioannis said:
For downcasts static_cast is better to be used. dynami_cast is for upcasting
and crosscasting.

"Upcasting" is never necessary, as far as I can tell. dynamic_cast is
used for checked downcasting (e.g., if I cast from Shape to Triangle,
ensures that the Shape really was a Triangle).

-Kevin
 
I

Ioannis Vranos

Kevin Goodsell said:
"Upcasting" is never necessary, as far as I can tell. dynamic_cast is
used for checked downcasting (e.g., if I cast from Shape to Triangle,
ensures that the Shape really was a Triangle).


Yes my silly mistake. Downcasting and crosscasting is dynamic_cast about.
Upcasting needs no casting.







Ioannis Vranos
 
S

Steven T. Hatton

Ioannis said:
Yes my silly mistake. Downcasting and crosscasting is dynamic_cast about.
Upcasting needs no casting.

OK, which way is up? Actually, I didn't even think about that issue. But now
that I think about it. A dynamic_cast would be the thing to use. Upcasting
actually means to cast from a derived to a base class, and _does_, I'm
pretty sure, require a static_cast.

This is a little exercise I made up to try to get a handle on all this.
It's a very tricky topic.
/* Hava Fun */
#include <iostream>
#include <string>
using std::eek:stream;
using std::string;
using std::cout;

class A{
public:
A(const string& name = "A"):m_name(name)
{}
virtual ostream& toString (ostream& out) const;
friend ostream& operator<<(ostream& out, const A& a);
protected:
string m_name;
};

ostream& A::toString (ostream& out) const{
return out << this->m_name;
}

ostream& operator<<(ostream& out, const A& a)
{
return a.toString(out);
}


class B:public A
{
public:
B(const string& name = "B"):A(name)
{}
ostream& toString (ostream& out) const;

};

ostream& B::toString (ostream& out) const{
A::toString(out);
return out << this->m_name;
}

ostream& operator<<(ostream& out, const B& b)
{
return b.toString(out);
}

int main()
{

A a;
B b;
A* aa_ptr = &a;
A* ab_ptr = &b;
B* bb_ptr = &b;
cout << "A* aa_ptr = &a: " << *aa_ptr << std::endl;
cout << "A* ab_ptr = &b: " << *ab_ptr << std::endl;
cout << "B* bb_ptr = &b: " << *bb_ptr << std::endl;

ab_ptr = static_cast<A*>(&b);
cout<<"ab_ptr = static_cast<A*>(&b): " << *ab_ptr << std::endl;

ab_ptr = dynamic_cast<A*>(&b);

cout << "ab_ptr = dynamic_cast<A*>(&b): " << *ab_ptr << std::endl;

cout << "cout <<(A)*ab_ptr << std::endl;: " <<(A)*ab_ptr << std::endl;

cout << "cout << static_cast<A>(*ab_ptr) << std::endl;: ";
cout <<static_cast<A>(*ab_ptr) << std::endl;

cout << "cout << *dynamic_cast<A*>(ab_ptr) << std::endl;: ";
cout << *dynamic_cast<A*>(ab_ptr) << std::endl;
}
 
S

Steven T. Hatton

Steven said:
class A{
public:
A(const string& name = "A"):m_name(name)
{}
virtual ostream& toString (ostream& out) const;

The next line is not necessary. It came from an earlier approach.
 
I

Ioannis Vranos

Steven T. Hatton said:
OK, which way is up? Actually, I didn't even think about that issue. But now
that I think about it. A dynamic_cast would be the thing to use. Upcasting
actually means to cast from a derived to a base class, and _does_, I'm
pretty sure, require a static_cast.


No it doesn't, else inheritance would not work properly:


class base
{
// ...
public:
virtual void something() { // .. }
};


class derived1: public base
{
// ...
};

class derived2: public base
{
// ...
};



void some_func(const base *p)
{
p->something();
}

int main()
{
derived 1 d;

some_func(&d);
}


This is a little exercise I made up to try to get a handle on all this.


To get handle of what.

It's a very tricky topic.
/* Hava Fun */
#include <iostream>
#include <string>
using std::eek:stream;
using std::string;
using std::cout;

class A{
public:
A(const string& name = "A"):m_name(name)
{}
virtual ostream& toString (ostream& out) const;
friend ostream& operator<<(ostream& out, const A& a);
protected:
string m_name;
};

ostream& A::toString (ostream& out) const{
return out << this->m_name;
}

ostream& operator<<(ostream& out, const A& a)
{
return a.toString(out);
}


class B:public A
{
public:
B(const string& name = "B"):A(name)
{}
ostream& toString (ostream& out) const;

};

ostream& B::toString (ostream& out) const{
A::toString(out);
return out << this->m_name;
}

ostream& operator<<(ostream& out, const B& b)
{
return b.toString(out);
}

int main()
{

A a;
B b;
A* aa_ptr = &a;
A* ab_ptr = &b;
B* bb_ptr = &b;
cout << "A* aa_ptr = &a: " << *aa_ptr << std::endl;
cout << "A* ab_ptr = &b: " << *ab_ptr << std::endl;
cout << "B* bb_ptr = &b: " << *bb_ptr << std::endl;

ab_ptr = static_cast<A*>(&b);
cout<<"ab_ptr = static_cast<A*>(&b): " << *ab_ptr << std::endl;

ab_ptr = dynamic_cast<A*>(&b);

cout << "ab_ptr = dynamic_cast<A*>(&b): " << *ab_ptr << std::endl;

cout << "cout <<(A)*ab_ptr << std::endl;: " <<(A)*ab_ptr << std::endl;

cout << "cout << static_cast<A>(*ab_ptr) << std::endl;: ";
cout <<static_cast<A>(*ab_ptr) << std::endl;

cout << "cout << *dynamic_cast<A*>(ab_ptr) << std::endl;: ";
cout << *dynamic_cast<A*>(ab_ptr) << std::endl;
}



I haven't drunk my coffee yet. May be later.







Ioannis Vranos
 
I

Ioannis Vranos

Ioannis Vranos said:
Yes my silly mistake. Downcasting and crosscasting is dynamic_cast about.
Upcasting needs no casting.


I took a look at TC++PL to refresh my memory and in summary dynamic_cast is
usually used for downcasts and crosscasts but the type of the object must be
polymorphic, that is to have virtual functions.

static_cast converts between related types, such as one pointer to another
in the same class hierarchy.

reinterpret_cast handles conversions between unrelated types.

dynamic_cast is a form of run-time checked conversion, and is usually used
for downcasts and crosscasts and the type of the object must be
*polymorphic*, that is to have virtual functions.


So if in an hierarchy and you want to perform (compile-time checked)
related- type conversion use static_cast. If you want to check at runtime if
the types are related use dynamic_cast provided that the object is
polymorphic if you want the cast to not indicate failure although it is
correct.


Examples:

#include <iostream>

class A
{
};

class B: public A
{
};

int main()
{
B b;

A *p=&b;


B *bp=dynamic_cast<B *>(p);

std::cout<<bp<<std::endl;
}

Executing: C:\Program Files\ConTEXT\ConExec.exe
"C:\MinGW\bin\g++.exe" -std=c++98 -pedantic-errors -O3 -Wall "temp.cpp" -o
temp

temp.cpp: In function `int main()':
temp.cpp:18: error: cannot dynamic_cast `p' (of type `class A*') to type
`class
B*' (source type is not polymorphic)
Execution finished.



#include <iostream>

class A
{
public:
virtual void something() const {}
};

class B: public A
{
};

int main()
{
B b;

A *p=&b;


B *bp=dynamic_cast<B *>(p);

std::cout<<bp<<std::endl;
}


Executing: C:\Program Files\ConTEXT\ConExec.exe
"C:\MinGW\bin\g++.exe" -std=c++98 -pedantic-errors -O3 -Wall "temp.cpp" -o
temp
Execution finished.





#include <iostream>

class A
{
};

class B: public A
{
};

int main()
{
B b;

A *p=&b;


B *bp=static_cast<B *>(p);

std::cout<<bp<<std::endl;
}


Executing: C:\Program Files\ConTEXT\ConExec.exe
"C:\MinGW\bin\g++.exe" -std=c++98 -pedantic-errors -O3 -Wall "temp.cpp" -o
temp
Execution finished.






Ioannis Vranos



Ioannis Vranos
 
I

Ioannis Vranos

The same put into shape:

I took a look at TC++PL to refresh my memory and in summary,


static_cast converts between related types, such as one pointer to another
in the same class hierarchy. The conversion is compile-time checked.

reinterpret_cast handles conversions between unrelated types.

dynamic_cast is a form of run-time checked conversion, and the type of the
object must be *polymorphic*, that is to have virtual functions.


So if in an hierarchy and you want to perform (compile-time checked) related
type conversion use static_cast.

If you want to check at runtime if the types are related use dynamic_cast
provided that the object is polymorphic.

If you want to convert between oranges and potatoes use reinterpret_cast.



Examples:

#include <iostream>

class A
{
};

class B: public A
{
};

int main()
{
B b;

A *p=&b;


B *bp=dynamic_cast<B *>(p);

std::cout<<bp<<std::endl;
}

Executing: C:\Program Files\ConTEXT\ConExec.exe
"C:\MinGW\bin\g++.exe" -std=c++98 -pedantic-errors -O3 -Wall "temp.cpp" -o
temp

temp.cpp: In function `int main()':
temp.cpp:18: error: cannot dynamic_cast `p' (of type `class A*') to type
`class
B*' (source type is not polymorphic)
Execution finished.



#include <iostream>

class A
{
public:
virtual void something() const {}
};

class B: public A
{
};

int main()
{
B b;

A *p=&b;


B *bp=dynamic_cast<B *>(p);

std::cout<<bp<<std::endl;
}


Executing: C:\Program Files\ConTEXT\ConExec.exe
"C:\MinGW\bin\g++.exe" -std=c++98 -pedantic-errors -O3 -Wall "temp.cpp" -o
temp
Execution finished.





#include <iostream>

class A
{
};

class B: public A
{
};

int main()
{
B b;

A *p=&b;


B *bp=static_cast<B *>(p);

std::cout<<bp<<std::endl;
}


Executing: C:\Program Files\ConTEXT\ConExec.exe
"C:\MinGW\bin\g++.exe" -std=c++98 -pedantic-errors -O3 -Wall "temp.cpp" -o
temp
Execution finished.






Ioannis Vranos
 
I

Ioannis Vranos

Ioannis Vranos said:
static_cast converts between related types, such as one pointer to another
in the same class hierarchy. The conversion is compile-time checked.

reinterpret_cast handles conversions between unrelated types.

dynamic_cast is a form of run-time checked conversion, and the type of the
object must be *polymorphic*, that is to have virtual functions.


So if in an hierarchy and you want to perform (compile-time checked) related
type conversion use static_cast.

If you want to check at runtime if the types are related use dynamic_cast
provided that the object is polymorphic.

If you want to convert between oranges and potatoes use reinterpret_cast.



And of course to convert from a derived pointer type to a base pointer type
there is no need to cast.






Ioannis Vranos
 
S

Steven T. Hatton

Ioannis said:
And of course to convert from a derived pointer type to a base pointer
type there is no need to cast.

I believe it depends on what the objective is. If you want to call methods
on the base class instead of on the derived class, I'm pretty sure you need
a static cast to the *object* (not the pointer) when you invoke the
function. If you just want to point to a derived object with a base class
pointer, then you are correct, there is no need to cast.

I haven't tried it yet, but I believe you may also be able to used a scope
resolution operator to directly invoke a baseclass function.
 
A

Alf P. Steinbach

* "Steven T. Hatton said:
I believe it depends on what the objective is. If you want to call methods
on the base class instead of on the derived class, I'm pretty sure you need
a static cast to the *object* (not the pointer) when you invoke the
function. If you just want to point to a derived object with a base class
pointer, then you are correct, there is no need to cast.

I haven't tried it yet, but I believe you may also be able to used a scope
resolution operator to directly invoke a baseclass function.

For a virtual function the scope resolution operator is the only way.

A cast won't make any difference for a virtual function.

Btw., the notion that a cast will make a difference for a virtual function is
very common, especially among Java programmers (it doesn't make a difference
in Java either), so you're absolutely not the first one to think so. ;-)
 
I

Ioannis Vranos

Steven T. Hatton said:
I believe it depends on what the objective is. If you want to call methods
on the base class instead of on the derived class, I'm pretty sure you need
a static cast to the *object* (not the pointer) when you invoke the
function.


You are talking to call base public member functions from the outside?
Because you can do Base::Func() from the inside.

For the outside you can do:


class base
{
public:
void whatever() const {}
};

class derived:public base
{
public:
void whatever() const { /*new definition*/ }
};


derived d;

base *p=&d;

base &r=d;


// They call base::whatever()
p->whatever();
r.whatever();






Ioannis Vranos
 
S

Steven T. Hatton

For a virtual function the scope resolution operator is the only way.

A cast won't make any difference for a virtual function.

Btw., the notion that a cast will make a difference for a virtual function
is very common, especially among Java programmers (it doesn't make a
difference in Java either), so you're absolutely not the first one to
think so. ;-)

Read what I wrote very carefuly. I didn't say 'casting the pointer'. Run
this code and look at the last few lines of output. Calling the function on
the _dereferenced_pointer_ without a cast invokes the derived version.
Calling it with a static_cast applied to the object _not_the_pointer_
invokes the base class function.

#include <sstream>
#include <iostream>
#include <string>
using std::eek:stream;
using std::string;
using std::cout;

class A{
public:
A(const string& name = "A"):m_name(name)
{}
virtual ostream& toString (ostream& out) const;

protected:
string m_name;
};

ostream& A::toString (ostream& out) const{
return out << "Class A.m_name = " << this->m_name << "; ";
}

ostream& operator<<(ostream& out, const A& a)
{
return a.toString(out);
}

class B:public A
{
public:
B(const string& name = "B"):A(name)
{}
ostream& toString (ostream& out) const;

};

ostream& B::toString (ostream& out) const{
A::toString(out);
return out << "Class B.m_name = " << this->m_name << "; ";
}

ostream& operator<<(ostream& out, const B& b)
{
return b.toString(out);
}

int main()
{

A a;
B b;
A* aa_ptr = &a;
A* ab_ptr = &b;
B* bb_ptr = &b;
cout << "A* aa_ptr = &a: " << *aa_ptr << "\n";
cout << "A* ab_ptr = &b: " << *ab_ptr << "\n";
cout << "B* bb_ptr = &b: " << *bb_ptr << "\n\n";

ab_ptr = static_cast<A*>(&b);
cout<<"ab_ptr = static_cast<A*>(&b):\n" << *ab_ptr << "\n\n";

ab_ptr = dynamic_cast<A*>(&b);

cout << "ab_ptr = dynamic_cast<A*>(&b):\n" << *ab_ptr << "\n\n";

cout << "cout << (A)*ab_ptr << \"\\n\";:\n" << (A)*ab_ptr << "\n\n";

cout << "cout << static_cast<A>(*ab_ptr) << \"\\n\";:\n";
cout << static_cast<A>(*ab_ptr) << "\n\n";

cout << "cout << *ab_ptr << \"\\n\";:\n";
cout << *ab_ptr << "\n\n";

cout << "cout << *dynamic_cast<A*>(ab_ptr) << \"\\n\";:\n";
cout << *dynamic_cast<A*>(ab_ptr) << "\n\n";
}
 
A

Alf P. Steinbach

* "Steven T. Hatton said:
Read what I wrote very carefuly. I didn't say 'casting the pointer'. Run
this code and look at the last few lines of output. Calling the function on
the _dereferenced_pointer_ without a cast invokes the derived version.
Calling it with a static_cast applied to the object _not_the_pointer_
invokes the base class function.

Ah, that is something else, yes. It is a _very_ dangerous and often also very
inefficient thing to do. You're casting an rvalue, yes, an rvalue, and this
is not just a convenient "tell the compiler what type it really is or should
be thought of as", it's an actual conversion of data. It's called a
slice-operation, and it's the same thing as passing a B instance by value
with expected type A, or initializing an A object from a B object. The
resulting value _is_ an A, there's no B part.

You can't do that in Java.

Do note that with a conforming compiler you're then not calling a function on
the original object. For static functions that doesn't make a difference.
But the inefficiency compared to simple qualification and the obfuscation
compared to directly declaring a temporary A means that this is not used.

Summing up: in practice it's meaningless.

Advice: never use it.
 
S

Steven T. Hatton

Alf said:
* "Steven T. Hatton" <[email protected]> schriebt:
You can't do that in Java.

There *are* some nice things about Java.
Do note that with a conforming compiler you're then not calling a function
on
the original object. For static functions that doesn't make a difference.
But the inefficiency compared to simple qualification and the obfuscation
compared to directly declaring a temporary A means that this is not used.

Summing up: in practice it's meaningless.

Advice: never use it.

The result of the code listed below was astonishing. Let me know what
results you get:

#include <iostream>
#include <string>
using std::eek:stream;
using std::string;
using std::cout;

class A{
public:
A(const string& name = "A"):m_name("A0_" + name)
{}
virtual ostream& toString (ostream& out) const;
virtual void setName (const string& name);
protected:
string m_name;
};

ostream& A::toString (ostream& out) const{
return out << "A.m_name = " << this->m_name << "; ";
}

void A::setName (const string& name)
{
this->m_name = "A1_"+name;
}

ostream& operator << (ostream& out, const A& a)
{
return a.toString(out);
}

class B: public A{
public:
B(const string& name = "B"):A(name),m_name("B0_"+name)
{}
virtual ostream& toString (ostream& out) const;
virtual void setName (const string& name);
protected:
string m_name;
};

ostream& B::toString (ostream& out) const{
A::toString(out);
return out << "B.m_name = " << this->m_name << "; ";
}


void B::setName (const string& name)
{
this->m_name = "B1_"+name;
}


ostream& operator<<(ostream& out, const B& b)
{
return b.toString(out);
}

int main()
{
A a;
B b;

A& aref = a;
aref = static_cast<A>(b);

A& abref = b;
abref = static_cast<A>(b);

cout << "aref = " << aref <<"\n";
cout << "abref = " << abref <<"\n";

A ainst = static_cast<A>(b);
cout << "ainst = " << ainst <<"\n";
}
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top