禁用模板化类中的函数

时间:2011-09-14 08:51:18

标签: c++ templates

我正在尝试禁用简单模板类中的某些函数。应该删除的函数取决于模板参数是否具有某些typedef。

这个例子归结为:

template<typename T>
struct Foo
{
  typename T::Nested foo() { return typename T::Nested(); }
  int bar() { return 1; }
};


struct NoNested
{
};

struct WithNested
{
  typedef int Nested;
};

int main()
{
  Foo<WithNested> fwn;
  fwn.foo();
  fwn.bar();

  Foo<NoNested> fnn;
  //fnn.foo();
  fnn.bar();
}

然而,这给了我关于gcc和clang ++的error: no type named ‘Nested’ in ‘struct NoNested’样式错误(请注意两者的旧版本)。

当typedef foo没有退出时,有一种简单的方法可以删除T::Nested吗? (除了Foo<T>类的模板特化之外,就像在实际代码中我有大约5个具有不同typedef的函数一样...这将导致2 ^ 5个不同的特化)

修改 既然有人要求想要这样做的动机: 我想在DSL中创建类似于编译时FSM的东西。

我希望能够做到这一点

struct StateA;
struct StateB;
struct StateC;

struct StateA
{
  typedef StateB AfterNext;
};

struct StateB
{
   typedef StateA AfterPrev;
   typedef StateC AfterNext;
};

struct StateC
{
   typedef StateB AfterPrev;
};

template<typename T>
struct FSM
{
   FSM<typename T::AfterNext> next() { return FSM<T::AfterNext>(); };
   FSM<typename T::AfterPrev> prev() { return FSM<T::AfterPrev>(); };
};

那样

FSM<StateA>().next().prev().next().next();

编译,但

FSM<StateA>().next().prev().prev();

失败。

请注意,实际上会有更多的转换函数,转换函数实际上会做一些事情,而FSM会存储一些状态。

更新: 我使用到目前为止给出的方法创建了proper examples。 答案的复杂性各不相同,虽然访问者方法是我可能最终使用的方法(因为它最简单),但我的解决方案(最复杂的)是唯一一个实际删除该功能的方法。

5 个答案:

答案 0 :(得分:4)

您可以使用类模板专门化。如果你有几个函数,那么你可以将每个函数移动到一个基类,并专门化每个基类。

答案 1 :(得分:1)

尝试自行制作功能foo模板。它只会在被调用时进行编译,因此只有在尝试使用NoNested类调用时才会出现错误。

答案 2 :(得分:1)

您可以为每个类添加嵌套的typedef,这样只有在实例化函数时编译才会失败。

struct null_type;  //an incomplete type, you could use a more descriptive name for your particular problem

template<typename T>
struct Foo
{
  typename T::Nested foo() { return typename T::Nested(); }
  int bar() { return 1; }
};


struct NoNested
{
   typedef null_type Nested;
};

struct WithNested
{
  typedef int Nested;
};

int main()
{
  Foo<WithNested> fwn;
  fwn.foo();
  fwn.bar();

  Foo<NoNested> fnn;
  //fnn.foo();  //attempt to use incomplete type when used
  fnn.bar();
}

答案 3 :(得分:1)

可以选择T::Nested类型(如果存在),否则选择void,如下所示。

默认选项为void

template<class T, class = void>
struct NestedReturn
{
  typedef void type;
};

一个总是返回void的模板,无论你给它什么类型:

template<class T>
struct Void
{
  typedef void type;
};

SFINAE具有Nested嵌套类的类型的特化。请注意,typename Void<typename T::Nested>::type始终无效,以匹配基本模板中void的默认第二个参数:

template<class T>
struct NestedReturn<T, typename Void<typename T::Nested>::type>
{
  typedef typename T::Nested type;
};

现在我们使用它。请注意,如果没有foo(),则T::Nested实际上不会被删除,但实例化它会导致错误。

template<typename T>
struct Foo
{
  typename NestedReturn<T>::type foo() { return typename T::Nested(); }
  int bar() { return 1; }
};


struct NoNested
{
};

struct WithNested
{
  typedef int Nested;
};

int main()
{
  Foo<WithNested> fwn;
  fwn.foo();
  fwn.bar();

  Foo<NoNested> fnn;
  //fnn.foo();
  fnn.bar();
}

我怀疑使用默认的函数模板参数可以使用SFINAE正确删除foo(),但这只能在C ++ 11中进行(未经测试的猜测):

template<typename T>
struct Foo
{
  template<class N = T::Nested>
  N foo() { return N(); }
  int bar() { return 1; }
};

答案 4 :(得分:0)

以下是我认为我可以解决的问题。它的灵感来自user763305的评论。 它需要2 * N的专业而不是2 ^ N。

template <typename T>
struct has_nested {
  // Variables "yes" and "no" are guaranteed to have different sizes,
  // specifically sizeof(yes) == 1 and sizeof(no) == 2.
  typedef char yes[1];
  typedef char no[2];

  template <typename C>
    static yes& test(typename C::Nested*);

  template <typename>
    static no& test(...);

  // If the "sizeof" the result of calling test<T>(0) would be equal to the sizeof(yes),
  // the first overload worked and T has a nested type named type.
  static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};



template<typename T>
struct FooBase
{
  int bar() { return 1; }
};

template<typename T, bool>
struct FooImpl : public FooBase<T>
{
};

template<typename T>
struct FooImpl<T,true> : public FooBase<T>
{
  typename T::Nested foo() { return typename T::Nested(); }
};


template<typename T>
struct Foo : public FooImpl<T, has_nested<T>::value >
{
};


struct NoNested
{
};

struct WithNested
{
  typedef int Nested;
};

int main()
{
  Foo<WithNested> fwn;
  fwn.foo();
  fwn.bar();

  Foo<NoNested> fnn;
  //fnn.foo();
  fnn.bar();

}