2

I am attempting to understand why casting from a base class to an derived class using pointers compiles fine, but casting using non-pointer object produces the error C2440.

Below I have a base class ThreadedMessage that is inherited by class GPSMessage.

struct ThreadedMessage { ThreadedMessage() : m_Type(0), m_ID(0) { } ThreadedMessage(uint Type, uint ID = 0) : m_Type(Type), m_ID(ID) { } uint m_Type; uint m_ID; }; struct GPSMessage : public ThreadedMessage { GPSMessage() : ThreadedMessage() { } GPSMessage(double lat, double lon) : ThreadedMessage(1), m_lat(lat), m_lon(lon) { } double m_lat; double m_lon; }; 

In myFunction I am attempting to cast from the base class to the derived class.

void myFunction(const ThreadedMessage& msg) { const GPSMessage* message = static_cast<const GPSMessage*>(&msg); // Compiles fine const GPSMessage message1 = static_cast<const GPSMessage>(msg); // Error C2440 } 

The call to myFunction() looks like this:

GPSMessage msg(21.123, 12.321); myFunction(msg); 

When I compile, the casting of a pointer compiles fine, but the non-pointer casting fails with the following error:

error C2440: 'static_cast' : cannot convert from 'const ThreadedMessage' to 'const GPSMessage' No constructor could take the source type, or constructor overload resolution was ambiguous

Why am I unable to cast from the base class to the derived class with non-pointer variables?

Compiler is MS VS 2008 C++.

Yes, I have looked at other similar SO questions, but the ones I have read don't seem to answer my question.

2
  • On a side note: did you consider upgrading MS VC 2008 to something which is a bit more modern? Newer versions of MSVC improved a lot on error reporting and following the C++ standard. Not to mention support for c++11, c++14 and c++17 Commented Nov 14, 2018 at 7:44
  • MS VS 2008 was the last version of VS to support the system I am working on. Also for many systems C++11 and later is not allowed or supported. Commented Dec 13, 2018 at 19:55

2 Answers 2

6

These two casts have differing meaning.

The first cast:

const GPSMessage* message = static_cast<const GPSMessage*>(&msg); // Compiles fine 

This means that msg is actually a GPSMessage (or a derived) object. You ask the compiler to threat msg as a GPSMessage. No new object will be created, message will point to msg. If msg is not actually a GPSMessage (or derived), then this cast has Undefined Behavior.

Btw, the following cast have the same meaning (casting to a reference):

const GPSMessage& message = static_cast<const GPSMessage&>(msg); // same meaning, which results in a reference instead of a pointer 

About the second cast:

const GPSMessage message1 = static_cast<const GPSMessage>(msg); // Error C2440 

This means, that you create a new object, message1 from msg. You should give a way to make this conversion possible. For example, you should create a constructor for GPSMessage which has a ThreadedMessage parameter:

struct GPSMessage { GPSMessage(const ThreadedMessage &); // needed constructor }; 

Or create a conversion operator for ThreadedMessage to GPSMessage:

struct ThreadedMessage { operator GPSMessage(); // conversion operator }; 
Sign up to request clarification or add additional context in comments.

Comments

2

In the function the compiler only knows that msg is a reference to a ThreadedMessage, it doesn't know what's really passed as an argument inside the function.

Think about what happened if you passed a reference to an actual ThreadedMessage object? Or a reference to another object from some other inherited class?

To cast from an ThreadedMessage object to a GPSMessage object, a conversion is needed, and there's no such conversion possible (the ThreadedMessage class doesn't have a conversion operator, and GPSMessage doesn't have a suitable constructor).

The only solution is to cast pointers or references to another pointer or reference. Like you do in the pointer example, or by doing e.g.

const GPSMessage& message1 = static_cast<const GPSMessage&>(msg); 

If you really want to cast to a GPSMessage object, not reference or pointer, then the best solution is to use a conversion constructor:

class GPSMessage : public ThreadedMessage { public: ... explicit GPSMessage(const ThreadedMessage& tm) : GPSMessage(static_cast<const GPSMessage&>(tm)) // Invoke copy-constructor {} ... }; 

Using the copy-constructor is nice, but it requires that the tm argument really is a reference to a GPSMessage object. Otherwise it's invalid.


Another solution is to make the classes polymorphic (simplest by making the destructors virtual) and use dynamic_cast to a pointer. If the result is a null pointer then msg wasn't a GPSMessage to begin with.

2 Comments

If one is very very sure of the type of msg, then const GPSMessage message1 = static_cast<const GPSMessage&>(msg); will work even without modifying GPSMessage.
Thanks for the reminder about virtual destructors. It reminded me that the all of the classes were missing virtual destructors.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.