2015-03-27 6 views
3

У меня есть некоторая концептуальная проблема в иерархии классов, где базовый класс зависит от фиксированного скалярного типа T, но производные классы CRTP используют метод вывода возвращаемого значения ,C++ смешивание строго типизированного базового класса с CRTP и вычитанием типа возвращаемого значения

Для примера рассмотрим следующую иерархию классов:

template<typename ... Args> struct VectorBase; 

template<typename T> 
struct VectorBase<T> 
{ 
    virtual T eval(int) const = 0; 
    auto operator[](int i) {return this->eval(i);} 
}; 

template<typename T, typename Derived> 
struct VectorBase<T, Derived> : public VectorBase<T> 
{ 
    virtual T eval(int i) const override final { return this->operator[](i); } 
    auto operator[](int i) const 
    { 
      return static_cast<Derived const&>(*this).operator[](i); 
    } 
}; 

template<typename T> 
struct Vector : public VectorBase<T, Vector<T> > 
{ 
    //just for code shortness, 
    //in reality there is a container which returns the corresponding elements 
    auto operator[](int i) const { return T{}; } 
}; 

template<typename VectorType> 
struct SomeTransformation : public VectorBase< /* ... what to write here generically? */ double, SomeTransformation<VectorType> > 
{ 
    VectorType const& v; 
    SomeTransformation(VectorType const& _v) : v(_v) {} 
    auto operator[](int i) const 
    { 
      //do something with vector v and return i-th element, e.g. 
      return v[i]*0.1; 
    } 
}; 

DEMO

Теперь, учитывая специфический вектор с типом значения int, скажем, можно применить SomeTransformation и получить вектор типа значения double , Кроме того, я могу быть уверен, что SomeTransformation происходит от VectorBase<double>, так что, к примеру, я не могу ложно назначить его VectorBase<int> -указатель:

int main() 
{ 
    Vector<int> v; 
    std::cout<<typeid(decltype(v[0])).name()<<std::endl; //prints "i" for int   

    auto u = SomeTransformation<decltype(v)>(v); 
    std::cout<<typeid(decltype(u[0])).name()<<std::endl; //prints "d" for double   

    //works 
    std::unique_ptr<VectorBase<double> > ud = std::make_unique<SomeTransformation<decltype(v)> >(v); 

    //gives a compile-time error, which is good 
    //std::unique_ptr<VectorBase<int> > ui = std::make_unique<SomeTransformation<decltype(v)> >(v);  
} 

Теперь проблема: в типе скалярного аргумента SomeTransformation , где я писал /* ... what to write here generically? */, я действительно хотел бы написать что-то вроде

template<typename VectorType> 
struct SomeTransformation : 
    public VectorBase<decltype(std::declval<SomeTransformation<VectorType> >().operator[](0)), SomeTransformation<VectorType> > 
{ 
    //... 
}; 

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

Есть ли способ получить это поведение , не нарушая иерархию наследования?

+0

Вы должны использовать иерархию наследования direcly для 'SomeTransformation'? Почему бы не написать 'SomeTransformation' самостоятельно (выполняя концепцию) и предоставить шаблон класса адаптера, который наследуется от' VectorBase <..>'? – dyp

+0

@ dyp: да, это сработало бы, и у меня тоже было это один раз, прежде чем я понял, что в моем случае довольно удобно сохранять отношения наследования ... в противном случае мне пришлось бы повторно реализовать несколько функций (например, «размер (), update (...) 'и т. д.), а также повторно объявить некоторые члены классов концепций. – davidhigh

+0

@ dyp: Я отправил ответ с некоторыми альтернативами, которые все еще имеют некоторые проблемы, посмотрите, хотите ли вы, thnx заранее. – davidhigh

ответ

0

Я сам вычислил возможную альтернативу и хочу обсудить ее.

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

Пример:

namespace detail 
{ 
    template<typename T, typename VectorType> 
    struct SomeTransformation : 
     public VectorBase<T, SomeTransformation<T, VectorType> > 
    { 
     //the same as above 
    }; 
} 

struct DummyType 
{ 
    //make any type convertible to DummyType 
    template<typename T> DummyType(T const&) {} 
}; 

template<typename VectorType> 
using SomeTransformationValueType = 
    decltype(std::declval<detail::SomeTransformation<DummyType, VectorType> >().operator[](0)); 

template<typename VectorType> 
using SomeTransformation = 
    typename detail::SomeTransformation<SomeTransformationValueType<VectorType>, VectorType>; 

DEMO

 Смежные вопросы

  • Нет связанных вопросов^_^