C++ 混合强类型基类与 CRTP 和返回值类型推导

Posted

技术标签:

【中文标题】C++ 混合强类型基类与 CRTP 和返回值类型推导【英文标题】:C++ mixing strongly typed base class with CRTP and return value type deduction 【发布时间】:2015-06-01 02:13:49 【问题描述】:

我在类层次结构中存在一些概念问题,其中基类依赖于固定标量类型T,但派生的 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&lt;double&gt;,因此,例如,我不能错误地将其分配给VectorBase&lt;int&gt;-指针:

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> >

     //...
;

为了自动推断转换的值类型并将该类型向下传播到基类。但是,这似乎不起作用,我认为这是因为基类是在派生类之前实例化的……所以我要推断其类型的类还不存在。

有没有办法在不破坏继承层次结构的情况下获得这种行为?

【问题讨论】:

您是否必须直接为SomeTransformation 使用继承层次结构?为什么不独立编写SomeTransformation(实现一个概念),并提供一个继承自VectorBase&lt;..&gt; 的适配器类模板? @dyp: 是的,这行得通,我也有过一次,在我意识到在我的情况下保持继承关系相当方便......否则我将不得不重新实现几个函数(如size(), update(...) 等),并在概念类中重新声明一些成员。 @dyp:我发布了一些替代方案的答案,但仍然存在一些问题,如果需要,请提前查看。 抱歉,我不太明白您的顾虑。你能举个例子吗?你能把基类分成一个接口(只有纯虚函数)和一个基类(有你想在派生类中使用的任何便利函数和成员)吗? @dyp:我不确定我是否说清楚了:我的目标是在基类中使用推导出的返回类型作为模板参数,同时保持继承层次结构。 【参考方案1】:

我自己想出了一个可能的替代方案,并想提出来讨论。

例如,可以将类型参数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

【讨论】:

以上是关于C++ 混合强类型基类与 CRTP 和返回值类型推导的主要内容,如果未能解决你的问题,请参考以下文章

C++重载重写重定义

C++笔记之多态

C++笔记之多态

C++笔记之多态

c++派生类的类型列表

C++从入门到入土第十八篇:多态