Concept 對 C++ 有什麼影響,它和 Rust 的泛型 Bound 和 Trait 有點類似?

C++ 標準里的 Concepts 是什麼?它對 C++ 有什麼影響?會不會因為又多了一個概念,讓它更難學了?

它好像和 Rust 的泛型 Bound 和 Traits 有點類似?還有,它和和 Haskell 有什麼聯繫?


在C++11移除Concept的時候,Michael Wong曾經說過一句話:"I am sure this will not be the last we will hear of it." 而在C++17加入Concept的時候,Eric Niebler發了這麼一條Twitter

對於C++的Concept來說,其核心觀點就在於對模板參數進行約束,抽象出來了一個Concept賦予模板參數進行約束(而我更認為是有了一層"類型系統"),例子可以參見我這裡的一個回答 電腦發展史上有哪些偉大的思想和技術? - 藍色的回答 ,而有了這層「類型系統」以後,你就可以如普通的類型一樣,進行類型重載,如

vector& v { ... };
multiset& s { ... };
auto vi = find(v, 7); // calls sequence overload
auto si = find(s, 7); // calls associative container overload

而非再考慮使用SFINAE。

有了這一層「類型系統」,也可以有效的改善錯誤提示信息,因為現在的模板參數有了「類型」了,而不是再一層一層的向下轉換,直到int, char等具體類型才停下來。而模板如今的痛苦早在Bjarne書寫《C++語言的設計與演化》的時候就意識到了,然而很遺憾的是,正式開始想辦法解決這個問題是直到2000年才開始,而更遺憾的是C++11把這個給斃掉了。所幸在Andrew Sutton, Bjarne, STL之父Alex,印第安納大學若干愛好者等人的努力下,重新設計Concept,最終在C++17進入。

對於Concept來說,很容易與Java, C#的interface比較,然而它們是不同的,因為Concept與Concept之間沒有interface那樣的繼承關係。

而有了Concept以後,STL也將會使用Concept重寫,所以即使你不直接使用Concept,你也能享受到Concept的好處。

而有關Concept的討論,我在幾個月前在Reddit上看到了一個非常好的討論,在這裡面包括了Concept的提案與GCC Concept的實現者Andrew Sutton的一些討論與回復,可以參考一下:Eric Niebler on Twitter: "The Concepts TS was voted out today! Concepts are (almost) an ISO standard. Congrats, A. Sutton. This will change everything. @isocpp #cpp" : cpp


不嚴格地講,Concepts Lite是個語法糖,包裝的是SFINAE這個已有feature。所以想弄清楚Concepts Lite,請自行學習SFINAE。當然type_traits也要看一下。

值得一提的是定義一個concepts就如同定義一個trait一樣。沒有任何新的語言特性混進來。這種設計滿足了大批有語義(不是語法)特性潔癖的C++程序員:)

此外,Concepts Lite只檢查caller side,不檢查definition,這是它和Rust的traits,Haskell的type class的一個不同。另外一個是用戶不需要顯式讓一個類型「實現」一個concept(因為concept就是type trait嘛),這點和Haskell不同;其差別類似Java的interface和Go的interface的差別。

可刷一遍這個:Concepts Lite: Constraining Templates with Predicates


好的。


為了拯救不開心...

詳情看未鵬的這篇blog http://blog.csdn.net/pongba/article/details/1726031


concept主要是為了解決sfinae的類型約束問題。

因為c++模板在編譯器不對模板約束條件做類型檢查,事實上也做不了那麼多類型檢查,只對實例化代碼進行類型檢查。就會有一些模板用了好一段時間沒問題,結果這次加上新類型的時候就報錯了的情況。

同時因為模板的泛型特徵不區分具體類型,只判斷能否編譯通過,就會產生很多潛在bug。

比如:

template&
void fun(T stream) { stream &<&< 1; }

理想的情況下是調用

std::fstream file;
fun(file);

可實際情況下,寫了

int i;
fun(i);

也能編譯通過,這就是sfinae產生的bug。

如果用concept解決這個問題,就要定義:

auto concept Constructable& {T();}

template& requires Constructable&
void fun(T stream) { stream &<&< 1; }

用構造函數來約束T的類型,int這種基本類型沒有構造函數,就會報錯。

而trait,則是用模板偏特化的方法,區分處理各種類型。

比如以上的bug,可以用

template &
struct is_int
{
static const bool value = false;
};
template &<&>
struct is_int&
{
static const bool value = true;
};

template&
void fun(T stream)
{
static_assert(true != is_int&::value, "not support int!");
stream &<&< 1; }

解決,is_int::value此時就是一個區分類型的trait。

同理可以推廣到其他類型上,此後

int i;
fun(i);

就會編譯期報錯了。

與interface面向類不同,concept是對模板的約束,並非強制類型檢查。

沒有implement全部介面的基類編譯不會通過,但是concept只要滿足約束條件即可使用模板,未滿足條件只是不能使用該模板而已。


推薦閱讀:

有哪些鍛煉編程能力的 C++ 項目?
Rust 和 C++ 有哪些優劣?
我是初學c++者,我想達到熟練使用c++的程度,我就想知道達到什麼地步才能算是熟練使用呢?
Unity 5 發布了,但是否 Unity 做的遊戲與 Unreal 相比要顯得粗糙很多?
GitHub 上都有哪些值得關注學習的有趣的 C++ 開源項目?

TAG:C | Haskell | Rust編程語言 |