Вопрос по templates, c++, crtp – Как передать параметры шаблона в CRTP?

3

В следующем коде:

template <typename T>
class CRTP
{
public:
};

template <int I, typename T>
class CRTPInt
{
public:
};

template <template <typename> class T>
class Derived : public T<Derived<T>>
{
public:
};

void main()
{
Derived<CRTP> foo;
Derived<CRTPInt<2>> foo2;
}

Как мне написать CRPTInt, чтобы я мог передать шаблонизированный параметр, который затем будет продолжен в определении Derived?

Спасибо,

Джим

Ваш Ответ

1   ответ
4

static polymorphism и способностьmixin (параметризованное) поведение. Чтобы проиллюстрировать две альтернативы, удобно сначала определить общий шаблон

template
<
        typename Derived
>
class enable_down_cast
{
private:
        // typedefs

        typedef enable_down_cast Base;

public:
        Derived const* self() const
        {
                // casting "down" the inheritance hierarchy
                return static_cast<Derived const*>(this);
        }

        // write the non-const version in terms of the const version
        // Effective C++ 3rd ed., Item 3 (p. 24-25)
        Derived* self()
        {
                return const_cast<Derived*>(static_cast<Base const*>(this)->self());
        }

protected:
        // disable deletion of Derived* through Base*
        // enable deletion of Base* through Derived*
        ~enable_down_cast() = default; // C++11 only, use ~enable_down_cast() {} in C++98
};

Затем вы определяете шаблон интерфейса класса для типа поведения, который вы хотите

template<typename FX>
class FooInterface
:
    // enable static polymorphism
    public enable_down_cast< FX >
{
private:
    // dependent name now in scope
    using enable_down_cast< FX >::self;

public:
    // interface
    void foo() { self()->do_foo(); }

protected:
    // disable deletion of Derived* through Base*
    // enable deletion of Base* through Derived*
    ~IFooInterface() = default; // C++11 only, use ~IFooInterface() {} in C++98/03
};

Чтобы получить разные реализации этого интерфейса, просто определите разные классы, каждый из которых является производнымFooInterface с самим собойcuriously recurring template parameters:

class FooImpl
:
    public FooInterface< FooImpl > 
{
private:
    // implementation
    friend class FooInterface< FooImpl > ;
    void do_foo() { std::cout << "Foo\n"; }
};

class AnotherFooImpl
:
    public FooInterface< AnotherFooImpl > 
{
private:
    // implementation
    friend class FooInterface< AnotherFooImpl >;
    void do_foo() { std::cout << "AnotherFoo\n"; }
};

Альтернативой является параметризация различных реализаций интерфейса. На этот раз шаблон класса зависит отtemplate-template parameter и не типовой параметр

template<template<int> class F, int X>
class BarInterface
:
    public enable_down_cast< F<X> >
{
private:
    // dependent name now in scope
    using enable_down_cast< F<X> >::self;

public:
    // interface
    void bar() { self()->do_bar(); }    

protected:
    // disable deletion of Derived* through Base*
    // enable deletion of Base* through Derived*
    ~BarInterface() = default; // C++11 only, use ~BarInterface() {} in C++98/03
};

Реализация - это еще один шаблон класса, который наследуется от интерфейса с самим собой и параметром не-типа в качестве аргументов.

template< int X >
class BarImpl
:
    public BarInterface< BarImpl, X > 
{
private:
    // implementation
    friend class BarInterface< ::BarImpl, X >;
    void do_bar() { std::cout << X << "\n"; }    
};

Вот как вы их называете:

int main()
{
    FooImpl f1;         
    AnotherFooImpl f2;
    BarImpl< 1 > b1;
    BarImpl< 2 > b2;

    f1.foo();
    f2.foo();
    b1.bar();
    b2.bar();

    return 0;
}

Классы в вашем вопросе не вполне вписываются в эту общую схему. Если вы можете датьDerived какое-то CRTP-подобное поведение, то вы можете сделать

class Derived1
: 
   public CRTP< Derived1 > 
{

};

template<int I>
class Derived2
: 
   public CRTPInt< Derived2, I >
{

};

UPDATE: На основе обсуждения изhttps://stackoverflow.com/a/11571808/819272Я обнаружил, что исходный ответ скомпилирован только в Visual Studio 2010, но не в gcc из-за некоторых непереносимых функций, характерных для Microsoft. Например.self() функция отenable_down_cast является (шаблоном) зависимым именем в своих производных классах, и, следовательно, не видимо без явногоusing директивы. Кроме того, я добавил дефолтных деструкторов с правильным уровнем защиты. Наконец, я переименовал свой оригинальный классenable_crtp вenable_down_cast потому что это именно то, что он делает: вручную включает статический полиморфизм, что компилятор делает автоматически для динамического полиморфизма.

Я надеялся избежать производного 2 решения. Я собираю объект модульной камеры, который позволяет политикам смешивать и сочетать линзы, тележки и т. Д. Я хотел бы иметь возможность указывать зум-объектив в качестве фокусного минимума и фокусного максимума, т.е. камеры & lt; Zoom & lt; 24,70 & gt ; & gt ;, но объективу в ответ требуется доступ к камере для вызова функций других модулей. (На самом деле в случае объектива это не так, но в Dollies и Heads они делают.) Tavison
@Tavison Для основанных на политике проектов вам не всегда нужен CRTP. Например. Вы можете напрямую получить из каждой политики:template<typename ZoomPolicy, typename DolliesPolicy> class Camera: public ZoomPolicy, pubic DolliesPlicy {}; Вместо этого вы можете позволить каждомуConcreteZoomPolicy вытекают из конкретногоZoomInterface и т.п.
Я поэкспериментирую с этим и приму ваш ответ, когда у меня получится. Я знал, что мне нужен еще один слой, но не нашел его. Tavison
@Tavison Чтобы продолжить, вы можете сделатьtemplate<typename Impl, int Min, int Max> class ZoomInterface: private enable_crpt< Impl<Min, Max> > {}; а затем есть реализации, такие какtemplate<int Min, int Max> class ZoomImpl: public ZoomInterface< ZoomImpl, Min, Max > {};, Тогда вы могли бы иметьCamera< ZoomImpl<24, 70> >
Это не компилируется в VC10. Я получаю эти ошибки. ошибка C2923: «F»; : "X" не является допустимым аргументом типа шаблона для параметра & lt; unonym-symbol & gt; см. декларацию «X»; см. ссылку на создание экземпляра шаблона класса BarInterface & lt; F, X & gt; ошибка при компиляции C3201: список параметров шаблона для шаблона класса «BarImpl»; не соответствует списку параметров шаблона для параметра шаблона "F"; см. ссылку на создание экземпляра шаблона класса "BarImpl & lt; X & gt;" компилируется Tavison

Похожие вопросы