[英]Disable functions inside templated class
我試圖在一個簡單的模板類中禁用一些函數。 應刪除的函數取決於模板參數是否具有某些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();
}
但是這給了我一個error: no type named 'Nested' in 'struct NoNested'
在gcc和clang ++上的error: no type named 'Nested' in 'struct NoNested'
樣式錯誤中error: no type named 'Nested' in 'struct NoNested'
(請注意兩者的舊版本)。
當typedef T::Nested
沒有退出時,是否有一種簡單的方法來刪除foo
? (除了Foo<T>
類的模板特化之外,在實際代碼中我有大約5個具有不同typedef的函數...這將導致2 ^ 5個不同的特化)
編輯:因為有一些人要求這樣做的動機:我想創建類似於編譯時間FSM的東西,以便在DSL中使用。
我希望能夠做到這一點
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會存儲一些狀態。
更新 :我已經使用到目前為止給出的方法創建了適當的示例 。 答案的復雜程度各不相同,雖然訪問者方法是我可能最終使用的方法(因為它最簡單),但我的解決方案(最復雜的)是唯一一個實際刪除該功能的方法。
您可以使用類模板專門化。 如果您有多個函數,則可以將每個函數移動到基類,並專門化每個基類。
嘗試自己制作函數foo
模板。 它只會在被調用時編譯,因此只有在嘗試使用NoNested
類調用它時才會出現錯誤。
您可以向每個類添加嵌套的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();
}
可以選擇類型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,以匹配基本模板中void
的默認第二個參數:
template<class T>
struct NestedReturn<T, typename Void<typename T::Nested>::type>
{
typedef typename T::Nested type;
};
現在我們使用它。 請注意,當沒有T::Nested
,實際上不會刪除foo()
,但實例化它會導致錯誤。
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; }
};
以下是我認為我可以解決的問題。 它的靈感來自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();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.