2013-12-18 81 views
4

在Java中,您可以限制泛型,以便參數類型只是特定類的子類。這允許泛型知道類型上的可用函數。將模板限制爲僅限某些類?

我還沒有看到這與C + +模板。那麼是否有限制模板類型的方法,如果不是,智能感知如何知道<typename T>可用的方法以及您的傳入類型是否適用於模板化函數?

+5

如果它走起來像鴨子,叫起來,並像鴨子,爲什麼不假裝這是一隻鴨子? –

+0

@BenjaminLindley沒有類型信息智能感知如何能夠自動完成功能/數據成員? (我的生活不取決於智能感知,但你知道 - 編譯器如何知道等) – user997112

+0

看看http://en.cppreference.com/w/cpp/header/type_traits,你可以使用is_same或is_base_of – gvd

回答

4

只是具體

#include <iostream> 
#include <typeinfo> 

template <typename T> 
struct IsSpecific 
{ 
    void name() const { std::cout << typeid(T).name() << std::endl; } 
}; 

template<typename T> struct Specific; 
template <> struct Specific<char> : public IsSpecific<char>{}; 
template <> struct Specific<int> : public IsSpecific<int> {}; 
template <> struct Specific<long> : public IsSpecific<long>{}; 

int main() { 
    Specific<char>().name(); 
    Specific<int>().name(); 
    Specific<long>().name(); 
    //Specific<std::string>().name(); // fails 
} 
+0

您是否可以包含一些簡短的類定義,以便我可以看到這與典型的類定義有何關係? – user997112

6

由於C++ 11,有沒有辦法約束模板類型參數。但是,您可以利用SFINAE確保僅爲特定類型實例化模板。請參閱std::enable_if的示例。您需要使用std::is_base_of

要啓用特定派生類的功能,例如,你可以這樣做:

template <class T> 
typename std::enable_if<std::is_base_of<Base, T>::value, ReturnType>::type 
foo(T t) 
{ 
    // ... 
} 

的C++工作組(特別是Study Group 8)正在試圖概念和約束添加到語言。這將允許您指定模板類型參數的需求。請參閱the latest Concepts Lite proposal。正如Casey在評論中提到的,Concepts Lite將作爲C++ 14的同一時間作爲技術規範發佈。

+1

Concepts Lite不會成爲C++ 14標準的一部分,它將在大致相同的時間框架內作爲單獨的技術規範發佈。有效的編譯器不需要將Concepts Lite實現爲符合標準,但那些感興趣的將有一個通用的規範來實現。 – Casey

+0

@Casey感謝您的更新。 –

+0

一般我不推薦SFINAE。在大多數情況下,它不適合工作,最終導致比解決問題更多的問題。 –

0

C++採用的實際類型系統的演變正在進行中,它的名稱爲concepts

新的C++ 1y概念可能會提供您正在尋找的內容,並且由於此功能已經計劃用於C++ 11,但沒有進入最終草案,因此有一個gcc fork以及一個實現這個概念。

對於現在的「窮人」解決方案,如果你想堅持標準給你的東西,就是使用type traits

4

Java泛型和C++模板是完全不同的東西,你可以做的最好的事情是避免嘗試一對一的映射。話雖如此,這個問題仍然有效,答案並不簡單。

大多數地方使用的簡單方法是對類型的要求是模板上合同的一部分。例如,std::sort要求前兩個參數的行爲如RandomAccessIterators這是一個文檔只有接口(屬性進行了描述,但代碼中沒有此機制)。然後模板只使用這些信息。

第二種最簡單的方法是記錄該合同並提供static_assert驗證可驗證的內容。

下一個可用的步驟是使用SFINAE,這是一種技術,您可以在其中強制編譯器檢查正在替換的類型的某些功能。如果替換(和檢查)失敗,則模板將被視爲無效並且編譯器繼續前進。我個人不鼓勵大多數SFINAE的使用,它是一個很好的工具,但它經常被濫用。

在未來的標準中,將會有更高層次的結構來通過概念對模板的參數強制執行某種形式的接口。問題在於,已經證明很難界定如何定義,檢測或驗證這些約束條件,直到找到一個好的解決方案,標準委員會不會爲一種明知的壞方法而解決。

所有的說法,我可能想回到這裏的第一段。 Java泛型和C++模板之間存在巨大差異。後者提供了一種稱爲編譯時多態性的東西。沒有真正的接口類型是在程序中的任何地方定義的[通常]沒有約束,即模板中使用的類型以任何可能的方式相關。

1

在C++中,如果您有一些模板參數T,則由於其使用方式而被有效地限制爲某些類集之一。例如,如果您在模板擴展中的某處引用了T::foo,則T不可能是沒有foo成員的類。或者假設T::foo確實存在,但是具有錯誤的類型;你的模板做了類似T::foo + 1,但是T::foo這是沒有算術。

如果T以各種方式滿足模板,並且得到的實例化是有意義的,那麼沒有理由擔心它。

在C++中能夠使用模板的類與任何方式都沒有關聯(即通過繼承)是一個重要的靈活性。

需要使用該模板的人只需編寫一個結構特徵與模板要求相匹配的類;模板的用戶不必從某些類型派生出來,只是將它們用作參數。

這種約束的唯一好處是更清晰的診斷。 T::foo不是以某種方式得到錯誤消息,而是您可能會得到「類型Widget與模板Xyz的參數2不匹配。」

但是,雖然更清楚,診斷的代價是接受這樣的理念,即這種不匹配實際上是真正的問題。 (如果程序員可以修復foo成員並使其工作,該怎麼辦?)

2

使用static_assertstd::is_base_of

#include <type_traits> 

class A { 
}; 

class B: public A { 
}; 

template <class T> 
class Class1 { 
    static_assert(std::is_base_of<A, T>::value, "T must be a descendant of A"); 
    ... 
    T foo(); 
    ... 
}; 


Class1<A> a; //it works 
Class1<B> b; //it works 
Class1<int> i; //compile error 
相關問題