C2440 static_cast не может преобразовать из базового класса в производный класс - PullRequest
0 голосов
/ 14 ноября 2018

Я пытаюсь понять, почему приведение из базового класса к производному классу с использованием указателей прекрасно компилируется, но приведение с использованием объекта без указателя выдает ошибку C2440.

Ниже у меня есть базовый класс ThreadedMessageкоторый наследуется классом 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;
};

В myFunction Я пытаюсь привести из базового класса к производному классу.

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
}

Вызов myFunction() выглядит так:

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

Когда я компилирую, приведение указателя компилируется нормально, но приведение без указателя завершается неудачно со следующей ошибкой:

ошибка C2440:'static_cast': невозможно преобразовать из 'const ThreadedMessage' в 'const GPSMessage' Ни один конструктор не может принять тип источника, или разрешение перегрузки конструктора было неоднозначным

Почему я не могу привести из базового класса кпроизводный класс с переменными без указателей?

Компилятор - MS VS 2008 C ++.

Да, я смотрел на другие подобные вопросы SO, но те, которые я прочитал, не кажутсяответить на мой вопрос.

Ответы [ 2 ]

0 голосов
/ 14 ноября 2018

Эти два приведения имеют различное значение.

Первое приведение:

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

Это означает, что msg на самом деле является GPSMessage (или производным) объектом.Вы спрашиваете компилятор об угрозе msg как GPSMessage.Новый объект не будет создан, message будет указывать на msg.Если msg на самом деле не является GPSMessage (или производным), то это приведение имеет неопределенное поведение.

Кстати, следующее приведение имеет то же значение (приведение к ссылке):

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

О втором приведении:

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

Это означает, что вы создаете новый объект message1 из msg.Вы должны дать возможность сделать это преобразование возможным.Например, вы должны создать конструктор для GPSMessage, который имеет параметр ThreadedMessage:

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

или создать оператор преобразования для ThreadedMessage в GPSMessage:

struct ThreadedMessage {
    operator GPSMessage(); // conversion operator
};
0 голосов
/ 14 ноября 2018

В функции компилятор знает только, что msg является ссылкой на ThreadedMessage, он не знает, что в действительности передается в качестве аргумента внутри функции.

Подумайте о том, что произошло, если вы передали ссылку на фактический ThreadedMessage объект? Или ссылка на другой объект из другого унаследованного класса?

Для приведения объекта ThreadedMessage к объекту GPSMessage необходимо преобразование, и такое преобразование невозможно (класс ThreadedMessage не имеет оператора преобразования, а GPSMessage - нет). есть подходящий конструктор).

Единственное решение - привести указатели или ссылки на другой указатель или ссылку. Как вы делаете в примере с указателем или, например,

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

Если вы действительно хотите привести к GPSMessage объекту , а не к ссылке или указателю, тогда лучшим решением будет использование конструктора преобразования:

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

Использовать конструктор копирования приятно, но для этого требуется, чтобы аргумент tm действительно был ссылкой на объект GPSMessage. В противном случае это недействительно.


Другое решение состоит в том, чтобы сделать классы полиморфными (проще всего сделать деструкторы virtual) и использовать dynamic_cast для указателя. Если результатом является нулевой указатель, то msg не был GPSMessage для начала.

...