2017-08-21 46 views
8

我一直在试用Curiously Recurring Template Pattern以获得一个通用的单参数仿函数,并且有两个实现:一个使用模板模板参数,第二个尝试访问在接口类中派生的Functor :: type。在后者的示例中,编译器(gcc 5.4.0)报告CTRP派生类中没有类型命名为'type'

错误:无类型命名为 '结构立方<双>'

template<class T, template<class> class Functor> 
class FunctorInterface_1 { 
private: 
    const Functor<T> &f_cref; 
public: 
    FunctorInterface_1() : f_cref(static_cast<const Functor<T>&>(*this)) {} 
    T operator() (T val) const { return f_cref(val); } 
}; // FunctorInterface_1 (works) 

template<class Functor> 
class FunctorInterface_2 { 
private: 
    const Functor &f_cref; 
public: 
    using Ftype = typename Functor::type; 
    FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {} 
    Ftype operator() (Ftype val) const { return f_cref(val); } 
}; // FunctorInterface_2 (no type in Functor!) 

I '类型'然后尝试在以下两个类的main()中使用T = double进行编译:

template<class T> 
struct Square : public FunctorInterface_1<T,Square> { 
    T operator()(T val) const { return val*val; } 
}; // Square 


template<class T> 
struct Cube : public FunctorInterface_2<Cube<T>> { 
    using type = T; 
    T operator() (T val) const { return val*val*val; } 
}; // Cube 

可以将FunctorInterface_2/Cube示例修改为可用,或者 在第一个示例中是否需要在T上将接口类模板化为 ?谢谢!

编辑:使用gcc -std = C++ 14,但我可以通过在FunctorInterface_1 :: operator()中使用自动返回和参数类型来获得第二个示例来编译并运行 ,类型不是C++ 14标准的一部分。

编辑2:嗯,我感觉有点厚。我只是意识到我可以在一个新参数上为FunctorInterface_1 :: operator()创建模板,然而,对于我所考虑的应用程序,我真的很希望我的基类能够访问派生类中定义的类型。

回答

4

当线路

using Ftype = typename Functor::type; 

在基类被处理,的Functor定义不可用。因此,您不能使用Functor::type

解决此限制的一种方法是定义特征类。

// Declare a traits class. 
template <typename T> struct FunctorTraits; 

template<class Functor> 
class FunctorInterface_2 { 
    private: 
     const Functor &f_cref; 
    public: 

     // Use the traits class to define Ftype 
     using Ftype = typename FunctorTraits<Functor>::type; 

     FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {} 
     Ftype operator() (Ftype val) const { return f_cref(val); } 
}; // FunctorInterface_2 (no type in Functor!) 

// Forward declare Cube to specialize FunctorTraits 
template<class T> struct Cube; 

// Specialize FunctorTraits for Cube 
template <typename T> struct FunctorTraits<Cube<T>> 
{ 
    using type = T; 
}; 

template<class T> 
struct Cube : public FunctorInterface_2<Cube<T>> { 
    using type = T; 
    T operator() (T val) const { return val*val*val; } 
}; // Cube 

工作代码:https://ideone.com/C1L4YW

+0

这是一个相当花哨的替代方法,直接将此类型作为OP在第一次执行时作为模板参数提供。我必须提到,如果type通过trait传递,那么派生类也必须从trait中提取它,也就是'using type = T;'应该是'使用type = FunctorTraits > :: type;'。否则,这种实施是非常脆弱的。 – VTT

+0

Traits方法对保持我的接口不可知是有吸引力的,但是,它似乎会增加开发派生类型的负担。我想到的应用程序涉及复杂的抽象矢量操作,它将有一些用于索引的OrdinalType和用于包含在其中的数据的一些ElementType。我也在探索一种纯粹的特质方法(而不是继承),但是我们正在探索我们目前实现的虚拟继承(copius动态向下转换!)方法的优点。 –

+0

@VTT,特征方法还有其他好处。例如。 Functor类不需要是类模板,就像OP使用的第一种方法一样。我同意第二点。然而,OP可能根本不需要它。 –

2

您的代码可以简化为

template<typename TDerived> class 
Base 
{ 
    using Ftype = typename TDerived::type; 
}; 

template<typename T> class 
Derived: public Base<Derived<T>> 
{ 
    using type = T; 
}; 

Derived<int> wat; 

它不会因为Base实例Derived类的工作点是不完整的,并且编译器不知道Derived::type的存在呢。

1

你必须明白,当你实例Cube<T>FunctionInterface_2<Cube<T>>最先被实例化。这意味着Cube<T>是一个不完整的类型,而这正在发生。
所以,当编译器到达使用Ftype = typename Functor::type;Functor是不完整的,你不能访问任何它的嵌套类型。

你的情况,你可以改变FunctionInterface_2到:

template<class Functor> 
class FunctorInterface_2 { 
private: 
    const Functor &f_cref; 
public: 
    FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {} 
    template <class TT> 
    auto operator() (TT && val) -> decltype(f_cref(val)) const { return f_cref(val); } 
}; 

所以现在访问有关Functor被延迟的信息,直到你从FunctionInterface_2此时FunctionInterface_2Cube完全实例调用operator()