Оператор преобразования реализован с использованием static_cast - PullRequest
7 голосов
/ 22 марта 2012

Я задаю этот вопрос после вопроса, который я поднял здесь .

Суть довольно проста.Предположим, у вас есть два класса этого типа:

template < class Derived >
class Base {
...
operator const Derived&() const {
    return static_cast< const Derived& >(*this);
  }
...
};

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

Затем предположим, что у вас есть преобразование типа, подобное этому:

template < class T >
functionCall( const Base<T>& param) {
  const T & val(param);
  ...
}

Вопрос: что должно быть стандартомсоответствующее поведение этого преобразования?

Должно ли оно быть таким же, как const T & val(static_cast<const T &> (param) ), или должно рекурсивно повторяться до переполнения стека?Обратите внимание, что я получаю первую компиляцию поведения с GNU g++, а вторую компиляцию с Intel icpc.

Я уже пытался взглянуть на стандарт (раздел 5.9 по static_cast и раздел 12.3 по преобразованиям), но из-заиз-за недостатка опыта я не смог найти ответ.

Заранее большое спасибо всем, кто нашел время, чтобы помочь мне с этим.

Ответы [ 2 ]

3 голосов
/ 22 марта 2012

Глядя на [expr.static.cast] в n3337 (первый рабочий проект после Стандарта):

2 / Значение l типа cv1 B, где B - тип класса, может быть приведено к типу «ссылка на cv2 D», где D - класс, полученный (раздел 10) из B, если существует допустимое стандартное преобразование из «указателя на D» в «указатель на B» [...]

4 / В противном случае выражение e может быть явно преобразовано в тип T с использованием static_cast формы static_cast<T>(e), если объявление T t(e); правильно сформировано, для некоторой придуманной временной переменной t [..]

Поэтому я бы интерпретировал, что поведение gcc является правильным, то есть выражение:

static_cast<Derived const&>(*this)

не должен вызывать рекурсивно operator Derived const& () const.

Я понял это из наличия ключевого слова В противном случае , которое подразумевает упорядочение правил. Правило 2/ следует попробовать перед правилом 4/.

0 голосов
/ 18 апреля 2012

Использование операторов неявного преобразования не рекомендуется.В C ++ 11 вы можете добавить ключевое слово explicit не только в конструкторы с одним аргументом, но и в операторы преобразования.Для кода C ++ 03 вы могли бы использовать явно преобразованную функцию преобразования, такую ​​как self() или down_cast().

Кроме того, вы, кажется, используете класс Base для CRTP, то есть для включения статическогополиморфизм.Это означает, что вы должны знать во время компиляции , какой именно Derived класс вы вызываете.Поэтому вам не нужно использовать ссылки const Base& в любом открытом коде, кроме как для реализации интерфейса CRTP.

В моих проектах у меня есть шаблон класса enable_crtp:

#include <type_traits>
#include <boost/static_assert.hpp>

template
<
        typename Derived
>
class enable_crtp
{
public:
        const Derived& self() const
        {
                return down_cast(*this);
        }

        Derived& self()
        {
                return down_cast(*this);
        }

protected:
        // disable deletion of Derived* through Base* 
        // enable deletion of Base* through Derived*
        ~enable_crtp()
        {
                // no-op
        }

private:
        // typedefs
        typedef enable_crtp Base;

        // cast a Base& to a Derived& (i.e. "down" the class hierarchy)
        const Derived& down_cast(const Base& other) const
        {
              BOOST_STATIC_ASSERT((std::is_base_of<Base, Derived>::value));
              return static_cast<const Derived&>(other);
        }

        // cast a Base& to a Derived& (i.e. "down" the class hierarchy)
        Derived& down_cast(Base& other)
        {
        // write the non-const version in terms of the const version
        // Effective C++ 3rd ed., Item 3 (p. 24-25)
        return const_cast<Derived&>(down_cast(static_cast<const Base&>(other)));
        }
};

Этот класс является частным производным от любого базового класса CRTP ISomeClass, например:

template<typename Impl>
class ISomeClass
:
    private enable_crtp<Impl>
{
public:
    // interface to be implemented by derived class Impl
    void fun1() const
    {
        self().do_fun1();
    }

    void fun2()
    {
        self().do_fun2()
    }

protected:
    ~ISomeClass()
    {}  
};

Различные производные классы могут реализовывать этот интерфейс по-своему, например:

class SomeImpl
:
    public ISomeClass<SomeImpl>
{
public:
    // structors etc.

private:
    // implementation of interface ISomeClass

    friend class ISomeClass<SomeImpl>;

    void do_fun1() const
    {
        // whatever
    }

    void do_fun2() 
    {
        // whatever
    }

    // data representation
    // ...
};
* 1022Вызов * внешнего кода fun1 из class SomeImpl будет делегирован соответствующей константной или неконстантной версии self() в class enable_crtp, и после down_casting будет вызвана реализация do_fun1.При наличии достойного компилятора все косвенности должны быть полностью оптимизированы.

ПРИМЕЧАНИЕ: защищенные деструкторы ISomeClass и enable_crtp делают код безопасным для пользователей, которые пытаются удалить объекты SomeImpl* через базовые указатели.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...