简体   繁体   中英

C2440 static_cast cannot convert from base class to derived class

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.

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
};

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 eg

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM