У меня есть некоторая концептуальная проблема в иерархии классов, где базовый класс зависит от фиксированного скалярного типа 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;
}
};
Теперь, учитывая специфический вектор с типом значения 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> >
{
//...
};
для того, чтобы автоматически вывести тип значения преобразования и размножающий этот тип вплоть до базового класса. Однако это, похоже, не работает, и я думаю, это потому, что базовые классы создаются до производного класса ... поэтому класс, который я хочу вывести, еще не существует.
Есть ли способ получить это поведение , не нарушая иерархию наследования?
Вы должны использовать иерархию наследования direcly для 'SomeTransformation'? Почему бы не написать 'SomeTransformation' самостоятельно (выполняя концепцию) и предоставить шаблон класса адаптера, который наследуется от' VectorBase <..>'? – dyp
@ dyp: да, это сработало бы, и у меня тоже было это один раз, прежде чем я понял, что в моем случае довольно удобно сохранять отношения наследования ... в противном случае мне пришлось бы повторно реализовать несколько функций (например, «размер (), update (...) 'и т. д.), а также повторно объявить некоторые члены классов концепций. – davidhigh
@ dyp: Я отправил ответ с некоторыми альтернативами, которые все еще имеют некоторые проблемы, посмотрите, хотите ли вы, thnx заранее. – davidhigh