12

我一直在尝试使用Curiously Recurring Template Pattern用于通用单参数仿函数,并有两种实现:一种使用有效的模板模板参数,另一种我尝试在接口类中访问派生的 Functor::type。在后一个示例中,编译器 (gcc 5.4.0) 报告

错误: “struct Cube< double >”中没有名为“ 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!)

然后我尝试在以下两个类的 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

4 回答 4

6

当线

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

于 2017-08-21T16:32:45.923 回答
3

您的代码可以简化为

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存在。

于 2017-08-21T16:32:26.117 回答
1

您必须了解,当您实例化时,Cube<T> FunctionInterface_2<Cube<T>>首先会实例化。这意味着Cube<T>发生这种情况时这是一个不完整的类型。
因此,当编译器到达行时 usingFtype = 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会延迟,直到您调用operator()fromFunctionInterface_2并完全实例化FunctionInterface_2Cube

于 2017-08-21T16:40:00.907 回答
1

注意: @r-sahu已经回答了这个问题,但我想详细说明一下并专门解决 clang 的输出。

可以在更小的代码示例上演示该问题:(@vtt提出了类似的建议)

template <typename _CRTP>
struct A {
    using _C = typename _CRTP::C;
};

struct B : public A<B> {
    using C = int;
};

用 clang 编译会导致完全误导性的错误消息:(godbolt

<source>:3:32: error: no type named 'C' in 'B'
    using _C = typename _CRTP::C;
               ~~~~~~~~~~~~~~~~^
<source>:6:19: note: in instantiation of template class 'A<B>' requested here
struct B : public A<B> {
                  ^
1 error generated.

GCC 的错误信息更有帮助:( godbolt )

<source>: In instantiation of 'struct A<B>':
<source>:6:19:   required from here
<source>:3:33: error: invalid use of incomplete type 'struct B'
    3 |     using _C = typename _CRTP::C;
      |                                 ^
<source>:6:8: note: forward declaration of 'struct B'
    6 | struct B : public A<B> {
      |        ^

正如接受的答案中所建议的那样,实现特征类型可以解决问题:

// this declaration must appear before the definition of A
template <typename _A>
struct a_traits;

template <typename _CRTP>
struct A {
    // `a_traits<_CRTP>::type` is an incomplete type at this point,
    // but that doesn't matter since `A` is also incomplete
    using _C = typename a_traits<_CRTP>::type;
};

// this specialization must appear before the definition of B
template <>
struct a_traits<struct B> { // adding the type specifier `struct` will declare B
    using type = int;
};

// specifying the template parameter will complete the type `A<B>`, which works since
// `a_traits<B>` is already complete at this point
struct B : public A<B> {
    using C = int;
};
于 2019-01-07T18:58:36.073 回答