2017-08-21 64 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()