Проверка типов C++ во время компиляции

все. Я новичок в C++ и пишу небольшую библиотеку (в основном для своих собственных проектов) на C++. В процессе проектирования иерархии типов я столкнулся с проблемой определения оператора присваивания.

Я использовал базовый подход, который в итоге был использован в этой статье. , то есть для каждого класса MyClass в иерархии, производной от класса Base, вы определяете два оператора присваивания следующим образом:

class MyClass: public Base {
public:
    MyClass& operator =(MyClass const& rhs);
    virtual MyClass& operator =(Base const& rhs);
};

// automatically gets defined, so we make it call the virtual function below
MyClass& MyClass::operator =(MyClass const& rhs);
{
    return (*this = static_cast<Base const&>(rhs));
}

MyClass& MyClass::operator =(Base const& rhs);
{
    assert(typeid(rhs) == typeid(*this)); // assigning to different types is a logical error
    MyClass const& casted_rhs = dynamic_cast<MyClass const&>(rhs);
    try {
        // allocate new variables
        Base::operator =(rhs);
    } catch(...) {
        // delete the allocated variables
        throw;
    }
    // assign to member variables
}

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

class MyClass: public Base {
public:
    operator =(MyClass const& rhs); // etc
    virtual inline MyClass& operator =(Base const& rhs)
    {
        assert(typeid(rhs) == typeid(*this));
        return this->set(static_cast<Base const&>(rhs));
    }
private:
    MyClass& set(Base const& rhs); // same basic thing
};

Но мне было интересно, могу ли я проверить типы во время компиляции. Я заглянул в Boost.TypeTraits и приблизился, выполнив BOOST_MPL_ASSERT((boost::is_same<BOOST_TYPEOF(*this), BOOST_TYPEOF(rhs)>));, но поскольку rhs объявлен как ссылка на родительский класс, а не на производный класс, он задохнулся.

Теперь, когда я думаю об этом, мои рассуждения кажутся глупыми — я надеялся, что, поскольку функция встроенная, она сможет сама проверять фактические параметры, но, конечно, препроцессор всегда запускается перед компилятором. Но мне было интересно, знает ли кто-нибудь о каком-либо другом способе принудительной проверки такого рода во время компиляции.


person Masterofpsi    schedule 12.04.2010    source источник
comment
Невозможно проверить типы во время компиляции, потому что в этом весь смысл полиморфизма: тип вещей определяется только во время выполнения. Animal* animal = rand() % 2 ? new Dog() : new Cat();   -  person UncleBens    schedule 12.04.2010
comment
Хорошая статья, кстати, спасибо за это :)   -  person Matthieu M.    schedule 12.04.2010


Ответы (2)


Вы не можете выполнить это утверждение во время компиляции по той простой причине, что типы времени выполнения не будут известны до, ну, во время выполнения.

assert(typeid(rhs) == typeid(*this));
return this->set(static_cast<Base const&>(rhs));

В не встроенной версии у вас было dynamic_cast. Я бы сохранил это, чтобы вы получали четко определенную ошибку, а не неопределенное поведение, если ваше утверждение нарушается.

Если вы сделаете это, утверждение будет либо чрезмерно ограничительным, либо бессмысленным. dynamic_cast вызовет исключение bad_cast как в отладочной, так и в релизной сборке. Это то, что вы хотите.

Лично я бы поставил под сомнение всю проблему полиморфного присваивания. Я бы последовал совету Скотта Мейерса по эффективному С++ и сделал все ваши нелистовые узлы в иерархии наследования абстрактными. Затем вы можете сделать операторы присваивания базового класса защищенными и невиртуальными.

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

person CB Bailey    schedule 12.04.2010
comment
+1 за оператор присваивания protected и конструктор копирования для неконечных классов. В любом случае всегда есть виртуальный метод clone для копирования. - person Matthieu M.; 12.04.2010

Если вы хотите, чтобы что-то определялось во время компиляции, то динамический полиморфизм (виртуальные функции и т. д.) вам не подходит. Обычно я не вижу необходимости смешивать динамический полиморфизм с вещами, которые есть у "обычных типов значений", такими как операторы присваивания. Возможно, в вашем случае лучше всего подходят конкретные и неполиморфные классы. Но трудно сказать, потому что вы ничего не сказали о том, что вы пытаетесь сделать с классами.

person sellibitze    schedule 12.04.2010