Соответствующий CRTP в классе шаблона - PullRequest
1 голос
/ 24 октября 2011

В последнее время я возился с шаблонами и наткнулся на следующую проблему.Я реализую шаблон CRTP следующим образом:

template<typename derived_t>
struct protocol_object
{
    ...
};

struct data_object : public protocol_object<data_object>
{
    ...
};

Теперь я хотел бы сопоставить экземпляры class protocol_object в функции шаблона члена, но при этом принимаю не CRTP-типы:

struct consumer_impl
{
    template<typename derived_t>
    void match(protocol_object<derived_t> &value)
    {
       std::cout << "protocol_class";
    };

    template<typename T>
    void match(T &value)
    {
       std::cout << "any other type";
    };
}

К сожалению, называется только вторая версия.Очевидно, match(protocol_object<derived_t> &value) не рассматривается или не отклоняется в пользу более общей формы match(T &value).

data_object object;
double value;
consumer_impl consumer;

consumer.match(value);  // yields "any other type" OK
consumer.match(object); // also yields "any other type" but want "protocol_class"

Есть ли выход из этого?

Спасибо за любые подсказки.Arne

Ответы [ 3 ]

2 голосов
/ 24 октября 2011

Это не связано с CRTP. Это общий случай следующего:

  • Разработайте шаблонную функцию так, чтобы все производные классы использовали определенную специализацию.

Проблема в том, что T& value является точным совпадением для Derived&, в то время как Base& является неточным совпадением. Поэтому мы сделаем общую форму хуже:

struct conversion_required { conversion_required(int) {} };

template<typename derived_t>
void match_impl(protocol_object<derived_t> &value, int)
{
   std::cout << "protocol_class";
};

template<typename T>
void match_impl(T &value, conversion_required)
{
   std::cout << "any other type";
};

template<typename T>
void match(T& value)
{
    return match_impl(value, 0);
}

Теперь специализация, требующая повышения, лучше соответствует общему шаблону, требующему пользовательского преобразования.

0 голосов
/ 24 октября 2011

Вторая функция лучше подходит, поскольку для нее не требуется преобразование, тогда как для первой функции требуется преобразование из производной в основание.

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

template <class T>
void
match (typename boost::enable_if_c
             <boost::is_base_of<protocol_object<T>,T>::value, T>::type& t)
{
    std::cout << "protocol_class";
}

template <class T>
void
match (typename boost::disable_if_c
             <boost::is_base_of<protocol_object<T>,T>::value, T>::type& t)
{
    std::cout << "any other type";
}

Это будет работать для всех классов T, полученных из protocol_object<T>, но не для самого protocol_object<T>. Вы можете добавить для него еще одну перегрузку (в основном, повторно использовать свою первую match функцию) или изменить условие в enable_if так, чтобы оно совпадало с protocol_object<T>.

0 голосов
/ 24 октября 2011

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

consumer.match(static_cast<protocol_object<data_object>&>(object));
...