深入理解c++20 concepts
concepts在c++20中被引入,其作用是對(duì)模板參數(shù)進(jìn)行約束,極大地增強(qiáng)了c++模板的功能。
在c++20之前,如果希望獲取類似的效果,使用起來并不方便。
沒有concept時(shí),如何實(shí)現(xiàn)對(duì)模板參數(shù)進(jìn)行約束?
static_assert
我們可以使用static_assert去對(duì)模板類型T進(jìn)行約束。如下所示:
#include <type_traits>
#include <iostream>
template<class T>
void test(T a)
{
? ? static_assert(std::is_integral<T>());
? ? std::cout << "T is integral" << std::endl;
}
int main()
{
? ? test(10);
? ? test<double>(12.3);?
}但是該種方法不太好,因?yàn)樾枰獙tatic_assert嵌入到函數(shù)的內(nèi)部,這意味著即使類型不對(duì),模板還是成功的匹配上了,只是在模板函數(shù)內(nèi)部展開時(shí)出現(xiàn)編譯錯(cuò)誤。
SFINAE
SFINAE是Substitution Failure Is Not An Error的縮寫,翻譯過來的意思是替換失敗并不是一個(gè)錯(cuò)誤。
SFINAE是模板元編程中常見的一種技巧,如果模板實(shí)例化后的某個(gè)模板函數(shù)(模板類)對(duì)該調(diào)用無效,那么將繼續(xù)尋找其他重載決議,而不是引發(fā)一個(gè)編譯錯(cuò)誤。
因此一句話概括SFINAE,就是模板匹配過程中會(huì)嘗試各個(gè)重載模板,直到所有模板都匹配失敗,才會(huì)認(rèn)為是真正的錯(cuò)誤。
例如下面這個(gè)經(jīng)典的例子:
struct Test {
? ? ?typedef int foo;
};
template <typename T>
void f(typename T::foo) {} ?// Definition #1
template <typename T>
void f(T) {} ?// Definition #2
int main() {
? ? f<Test>(10); ?// Call #1.
? ? f<int>(10); ? // Call #2. Without error (even though there is no int::foo)
? ? ? ? ? ? ? ? // thanks to SFINAE.
}f<Test>(10)最終將使用到第一個(gè)模板定義, 而f<int>(10)最終將使用到第二個(gè)模板定義。
SFINAE 原則最初是應(yīng)用于上述的模板編譯過程。后來被C++開發(fā)者發(fā)現(xiàn)可以用于做編譯期的決策,配合sizeof可以進(jìn)行一些判斷:類是否定義了某個(gè)內(nèi)嵌類型、類是否包含某個(gè)成員函數(shù)等。例如STL中迭代器中的has_iterator_category。
template <typename T>
struct has_iterator_category {
? ? struct two { char a; char b; };
? ? template <typename C>
? ? static two& test(typename C::iterator_category*);
? ? template <typename>
? ? static char& test(...);
? ? static const bool value = sizeof(test<T>(nullptr)) == sizeof(two);
};enable_if
enable_if的出現(xiàn)使得SFINAE使用上更加方便,進(jìn)一步擴(kuò)展了上面has_xxx,is_xxx的作用。而enable_if實(shí)現(xiàn)上也是使用了SFINAE。
enable_if的定義簡單, 即當(dāng)_Test是true時(shí),將不會(huì)有type的類型定義,而當(dāng)_Test是false時(shí),將會(huì)有type的類型定義。
// STRUCT TEMPLATE enable_if
template <bool _Test, class _Ty = void>
struct enable_if {}; // no member "type" when !_Test
template <class _Ty>
struct enable_if<true, _Ty> { // type is _Ty for _Test
? ? using type = _Ty;
};下面是利用enable_if去實(shí)現(xiàn)SFINAE的方式。
#include <type_traits>
#include <iostream>
template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type?
test(T value)
{
? ? std::cout<<"T is intergal"<<std::endl;
? ? return value;
}
template <typename T>
typename std::enable_if<!std::is_integral<T>::value, T>::type?
test(T value)
{
? ? std::cout<<"T is not intergal"<<std::endl;
? ? return value;
}
int main()
{
? ? test(100);
? ? test('a');
? ? test(100.1);
}可以看到SFINAE的主要實(shí)現(xiàn)了is_xxx和has_xxx的語義,但是其語法并不簡單,對(duì)使用者有較高要求,并且即便寫正確了,可讀性也相對(duì)較差。
有了concept之后如何使用?
聲明concept
聲明一個(gè)concept的語法如下所示:
template < template-parameter-list > concept concept-name = constraint-expression;
例如,約束T是一個(gè)整形:
template <typename T> concept integral = std::is_integral_v<T>;
也可以使用requires更加靈活地定義concept,例如下面的例子要求T類型擁有一個(gè)名字叫做print的方法,另外需要擁有一個(gè)toString方法,并且返回值是string類型。
template <typename T>
concept printable = requires(T t) {
t.print(); //1
{t.toString()} -> std::same_as<std::string>; //2
};使用concept
使用concept有三種方式:
方法1:直接將concept嵌入模板的類型的尖括號(hào)<>內(nèi)
template<Arithmetic T>
void f(T a){/*function definition*/};
we can enforce it just after template declaration using requires:方法2:在模板聲明的下方使用requires關(guān)鍵字
template<class T>
requires Arithmetic<T>
void f(T a)
{/*function definition*/};
or after the function declaration方法3:使用后置形式,直接在函數(shù)聲明的后方使用requires關(guān)鍵字添加約束。
#include<concepts>
template<class T>
void f(T a) requires integral<T> // integral is in header <concepts>
{/*function definition*/}; 此外,concept還可以使用邏輯運(yùn)算符 && 和 ||。例如:
template <class T> concept Integral = std::is_integral<T>::value; template <class T> concept SignedIntegral = Integral<T> && std::is_signed<T>::value; template <class T> concept UnsignedIntegral = Integral<T> && !SignedIntegral<T>;
下面是兩個(gè)完整的例子來看看concepts是如何實(shí)現(xiàn)了is_xxx和has_xxx的功能。
第一個(gè)例子中test模板T只能是整形或者是double。實(shí)現(xiàn)了is_xxx。
#include <iostream>
template <class T>
concept Integral = std::is_integral<T>::value;
template <class T>
concept IsDouble = std::is_same<T, double>::value;
template<Integral T>
void test(T a)
{
? ? std::cout << "test(int) functin called" << std::endl;
}
template<IsDouble T>
void test(T a)
{
? ? std::cout << "test(double) functin called" << std::endl;
}
int main()
{
? ? test(100);
? ? test(10.0);
}第二個(gè)例子中print函數(shù)要求t擁有print函數(shù)。實(shí)現(xiàn)了has_xxx。
#include <iostream>
template <typename T>
concept Has_print = requires(T t) {
? ? t.print(); //1
};
class HasPrint
{
public:
? ? void print(){};
};
class NoPrint
{
};
template<Has_print T>
void print(T t)
{
? ? t.print();
}
int main()
{
? ? HasPrint t;
? ? print(t);
? ? NoPrint t2;
? ? print(t2); //將報(bào)編譯錯(cuò)誤
}總結(jié)
c++20的concepts增強(qiáng)了模板對(duì)于參數(shù)類型約束的功能,語法簡單,可以提高代碼的可讀性。
如果你的項(xiàng)目不能使用較新的標(biāo)準(zhǔn),那么還是要老老實(shí)實(shí)的使用SNINAE,enable_if那一套東西。
到此這篇關(guān)于深入理解c++20 concepts的文章就介紹到這了,更多相關(guān)c++20 concepts內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VC++實(shí)現(xiàn)View內(nèi)容保存為圖片的方法
這篇文章主要介紹了VC++實(shí)現(xiàn)View內(nèi)容保存為圖片的方法,涉及VC++中Bitmap類的save方法相關(guān)使用技巧,需要的朋友可以參考下2016-08-08
c++ 構(gòu)造函數(shù)中調(diào)用虛函數(shù)的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄猚++ 構(gòu)造函數(shù)中調(diào)用虛函數(shù)的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-12-12
Clion(CMake工具)中引入第三方庫的詳細(xì)方法
這篇文章主要介紹了Clion(CMake工具)中引入第三方庫的詳細(xì)方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02
C++詳解如何實(shí)現(xiàn)兩個(gè)線程交替打印
這篇文章主要介紹了使用C++庫實(shí)現(xiàn)兩個(gè)線程交替打印,一個(gè)線程打印奇數(shù)、一個(gè)線程打印偶數(shù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
關(guān)于C語言動(dòng)態(tài)內(nèi)存管理介紹
大家好,本篇文章主要講的是關(guān)于C語言動(dòng)態(tài)內(nèi)存管理介紹,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下2022-01-01
重啟后nvidia-smi命令不可執(zhí)行出現(xiàn)“Make?sure?that?the?latest?NVIDIA?
這篇文章主要介紹了重啟后nvidia-smi命令不可執(zhí)行,出現(xiàn)“Make?sure?that?the?latest?NVIDIA?driver?is?installed?and?running.”問題,本文給大家分享最新完美解決方法,需要的朋友可以參考下2022-12-12
C語言 fseek(f,0,SEEK_SET)函數(shù)案例詳解
這篇文章主要介紹了C語言 fseek(f,0,SEEK_SET)函數(shù)案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
Qt+Quick實(shí)現(xiàn)播放音樂和視頻的開發(fā)
這篇文章主要為大家詳細(xì)介紹了如何利用Qt+Quick實(shí)現(xiàn)播放音樂和視頻的開發(fā),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-03-03

