Почему преобразование static_cast из базы в производную работает внутри базы, а не снаружи - PullRequest
0 голосов

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

#include <iostream>
using std::cout;

class Base
{
public:
    template <typename T>
    int getValue() const { return static_cast<const T&>(*this).getValue(); }
};

class Derived: public Base
{
public:
    Derived(int v): value(v) { }
    int getValue() const { return value; }
    int value;
};

class Another
{
    int getValue() const { return 5; }
};

template <typename T>
void out(const Base & base) {
    cout << base.getValue<T>() << '\n';
}

int main() {
    Derived d(5);
    Base b;
    out<Derived>(d);    //understandable, d has derived part.
    out<Derived>(b);   //don't understand, b is only base.
    out<Another>(b);    //compile time error   
    //static_cast<Derived>(b);   //compile time error
}

Я прочитал эту статью насчет CRTP и наткнуться на этот код:

template <typename T>
class Base
{
public:
    void doSomething()
    {
        T& derived = static_cast<T&>(*this);
        use derived...
    }
};

class Derived : public Base<Derived>
{
    ...
};

И я не совсем понимаю, как здесь работает конвертация.

Ответы [ 3 ]

1 голос
/ 23 марта 2020

Преобразование static_cast должно использоваться, только если это преобразование является законным. В своем коде вы создаете объект класса Base и пытаетесь преобразовать его в класс Derived. К счастью, реализация Derived::getValue() не использует никаких элементов данных и возвращает значение из литерала. В любом случае это неопределенное поведение.

В случае CRTP экземпляр класса Base не создается: используются только экземпляры Derived.

Upd. Попробуйте это:

//static_cast<Derived>(b);   doesn't compile
static_cast<Derived&>(b);   shall compile

Upd 2. Вы получаете нежелательную информацию, потому что Derived::getValue() использует элемент данных (в вашей первоначальной версии кода элементы данных не использовались).

1 голос
/ 23 марта 2020

Это часть правил C ++. static_cast может использоваться для преобразования выражения базового класса в производный класс. Если во время выполнения объект на самом деле не является подобъектом базового класса объекта производного класса, тогда это неопределенное поведение без необходимости диагностики c.

Первое предложение вашего вопроса неверно, это приведение может записывается в любой точке кода.

out<Another>() не компилируется, поскольку Another не наследуется по Base.

1 голос
/ 23 марта 2020

Последнее приведение в main () неверно синтаксически и не эквивалентно коду в шаблоне, вы не можете вывести объект из объекта (вы можете выполнить переход вниз, вызывая сокращение типа). В приведенных выше шаблонах вы разыгрываете ссылки.

Derived& может быть привязано к Base&, static_cast не имеет возможности проверить это. CRTP обеспечивает это, поскольку this указывает на хранилище производного типа, *this дает ссылку, которую можно безопасно преобразовать в Derived& эталонный объект.

Ссылка на Another не может быть привязана к ссылка на Base, когда Base не является базовым классом Another. В этом случае приведение указателей или ссылок с использованием static_cast недопустимо.

Код шаблона допустим, в случае работы CRTP, потому что код шаблона устанавливается там, где Derived имеет достаточно полный тип, т. Е. Где использовался шаблон. Сам шаблон ничего не генерирует и не компилируется, проверяется только на корректность.

Тем не менее, в CRTP некоторые вещи не возможны, например, использовать внутри декларации вложенного типа базового класса из класса Derived как полные типы по простой причине: они не являются полными и не подлежат прямому поиску, в отличие от переменных-членов и функций. Если такое использование требуется, то третий должен быть определен перед Base, ограничивая требуемые объявления.

...