我正在尝试禁用简单模板类中的某些函数。应该删除的函数取决于模板参数是否具有某些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。 答案的复杂性各不相同,虽然访问者方法是我可能最终使用的方法(因为它最简单),但我的解决方案(最复杂的)是唯一一个实际删除该功能的方法。
答案 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();
}