轉(zhuǎn)眼間私痹,C++20的標準已經(jīng)發(fā)布快兩年了踢星。不少C++的開源項目也已經(jīng)將標準升級到最新的C++20了,筆者也開啟了新標準的學(xué)習歷程了表牢。所以借這系列的博文窄绒,記錄下筆者學(xué)習新標準的一些心得與吐槽~~
作為C++20系列的第一篇開篇之文,就要從千呼萬喚始處理的concept
聊起了崔兴,后續(xù)很多新的feature的實現(xiàn)彰导,也仰賴新的concept
的實現(xiàn),后續(xù)筆者的文章也會逐步展開敲茄。OK位谋,開始我們C++20旅程的第一站:concept
1.First Look
先從一個群友的一個實際的問題出發(fā),我們來看看concept
可以解決什么問題堰燎。是怎么樣通過coding實現(xiàn)的掏父。
-
SFINAE
熟悉C++模板編程的小伙伴肯定第一時間想到通過SFINAE的方式來解決,讓筆者來解決這個問題的話秆剪,會寫出下面的代碼:
template <typename T>
T test(T a) {
static_assert(!std::is_same_v<void, decltype(a + a)>);
static_assert(!std::is_same_v<void, decltype(a - a)>);
static_assert(!std::is_same_v<void, decltype(a * a)>);
static_assert(!std::is_same_v<void, decltype(a / a)>);
return a;
}
這里寫的代碼是一個略微Trick的表達赊淑,利用decltype
去獲取操作符計算后的類型,然后用std::is_same_v
進行一個其實沒什么意義的類型比較仅讽,來滿足static_assert
的語義陶缺,最終滿足我們對模板類型T
的一些約束。
-
concept
顯然上面的方式是很不直觀的洁灵,雖然能達到咱們的目的饱岸,但是從代碼優(yōu)雅角度來說是一種較差的選擇實現(xiàn)。
我們來看一下用C++20提供給我們的Concept是如何解決這個問題的:
template <typename T>
concept Cal = requires (T a) {
a + a;
a - a;
a * a;
a / a;
};
template <typename T>
requires Cal<T>
T test(T a) {
return a;
}
這是通過concept
來實現(xiàn)的一個類型約束方式徽千,Cal
代表著一個concept的實現(xiàn)苫费,requires
中花括號的內(nèi)容就代表了對于類型T
的約束,要滿足下面的操作符
a + a;
a - a;
a * a;
a / a;
Bingo罐栈! 似乎C++20給了我們一個更好的trait黍衙,接著往下看,我們繼續(xù)來細探Concept的實現(xiàn)荠诬。
2. How to use
- concept的定義
這里寫了一個例子琅翻,咱們基于這個例子來展開:
template <typename T>
concept Cal = requires (T a) {
a + a;
typename T::type;
{a + a} -> std::same_as<float>;
require Concept2<T>;
};
這里定義了4個requires的要求,只有滿足這4個條件才能通過concept
的限制柑贞,正常進行編譯方椎。
1). a + a
這個是最簡單的,該表達式能通過編譯則代表符合要求钧嘶,這里不會進行實際的計算棠众。
2). typename T::type
代表需要,T
類型定義了type
類型,才符合要求
3). {a + a} -> std::same_as<float>
這里的{}
代表了decltype(a + a)
之后的類型需要滿足后面的concept
的需求闸拿。這是一個語法糖空盼,也可以通過這樣來實現(xiàn):requires std::is_floating_point_v<decltype(a + a)>
4). 最后的require Concept2<T>
這代表了concept
的嵌套邏輯。requires
后面可以帶任意的concept
- concept的使用
了解了concept
定義之后新荤,我們就可以利用concept
來進行模板類型的約束了揽趾。
這里“回字有四種寫法”,大家可以選擇自己喜歡的方式來使用苛骨。(真搞不懂搞這么多寫法干什么篱瞎,不能統(tǒng)一一下嗎?痒芝, 下面介紹的順序隨筆者的偏好以此遞減)
template<typename T>
requires Cal<T>
T test(T a)
{
return a + a;
}
1). 這是筆者最認可的一種書寫方式俐筋,語義明確,在模板類型定義之后明確對它的要求严衬。
template<Cal T>
T test(T a)
{
return a + a;
}
2). 也還行吧澄者,模板參數(shù)前指定了concept
,也比較明確直觀瞳步。
Cal test(Cal a)
{
return a + a;
}
3). concept
命名一長就顯得有些瑣碎了闷哆,并且把concept
當類型使用了,看起來有些怪異了单起。
template<typename T>
T test(T a) requires Cal<T>
{
return a + a;
}
4). 把concept
寫后面的都是異端,當然如果你喜歡的話劣坊,也ok嘀倒。
3. concept的本質(zhì)
concept本質(zhì)上是:一個可以套用在模板上的constexpr bool
值。
相信下面這段代碼能幫助你更好的理解它:
template <typename T>
concept Con = std::is_floating_point_v<T>;
int main(int argc, char* argv[]) {
static_assert(std::is_same_v<decltype(Con<float>), bool>);
std::cout << Con<int> << std::endl;
return 0;
}
顯然局冰,上面的代碼我們用is_same_v
確定了concept
生成的類型就是bool
類型测蘑。而同樣的,在運行期康二,咱們也可以將concept
的結(jié)果作為一個bool
常量進行使用碳胳,并打印。
所以沫勿,take it easy挨约。 concept
很簡單,它只是C++20給你提供的一個better的工具产雹,來擺脫被瘋狂的模板報錯所支配的恐懼诫惭。但即使你完全不了解它,使用老的方式蔓挖,依然能夠同樣解決問題夕土。
4.小結(jié)
C++的一些模板推斷的錯誤常常讓人抓狂。而很多時候我們使用它需要
- 要進行模板推斷類型的編程設(shè)計
- 利用SFINAE的方式來類型約束
這無形之中增加Coding時的心智成本瘟判,而concept
作為一個新的語法糖怨绣,給了我們拆分二者的機會:讓上帝歸上帝角溃,凱撒歸凱撒。
使用好concept
來進行類型約束篮撑,enjoy新標準帶來的便利吧减细。
希望大家能夠有所收獲,筆者水平有限咽扇。成文之處難免有理解謬誤之處邪财,歡迎大家多多討論,指教质欲。