C2440 static_cast cannot convert from base class to derived class

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-11 05:35:26

问题


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.


回答1:


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



回答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.



来源:https://stackoverflow.com/questions/53294643/c2440-static-cast-cannot-convert-from-base-class-to-derived-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!