Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
nakameguro_feature.cpp vol17 落ち穂拾い:メタ関数編
Search
Cranberries
March 14, 2019
0
510
nakameguro_feature.cpp vol17 落ち穂拾い:メタ関数編
Cranberries
March 14, 2019
Tweet
Share
More Decks by Cranberries
See All by Cranberries
回復職上級講座
loligothick
0
590
Types for Units of Measurement
loligothick
0
1.4k
C++でもRustのResultが使いたい
loligothick
0
1.8k
string_view.pdf
loligothick
0
610
Defense Against Undefined Behavior -未定義動作に対する防衛術-
loligothick
2
1.5k
Featured
See All Featured
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
23
1.9k
Practical Orchestrator
shlominoach
185
10k
Ruby is Unlike a Banana
tanoku
96
10k
Building Better People: How to give real-time feedback that sticks.
wjessup
357
18k
Designing on Purpose - Digital PM Summit 2013
jponch
113
6.6k
Rails Girls Zürich Keynote
gr2m
93
13k
Keith and Marios Guide to Fast Websites
keithpitt
408
22k
Side Projects
sachag
451
42k
Intergalactic Javascript Robots from Outer Space
tanoku
266
26k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
13
430
RailsConf 2023
tenderlove
16
720
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
353
29k
Transcript
<metaname= description <metaname= description content = 落ち穂拾い"/> content = 落ち穂拾い"/>
いなむのみたま いなむのみたま
Variabletemplatesfortraits Variabletemplatesfortraits
要するに_v 要するに_v メタ関数の_tと同様に、::valueを書かずにすむやつ。 例: ::valueは_tと違ってtypenameキーワードいらないので恩恵が⼩さい けど、まああれば使いますよね。 //sameasstd::is_same<int,int>::value std::is_same_v<int,int>
std::is_aggregate<T> std::is_aggregate<T> 型Tがaggregateかどうかを判定するメタ関数。 以上! Preconditions std::remove_all_extents_t<T>が完全型であるか、 (cv修飾された)void型であること。
ところで…… ところで…… aggregateって知ってる? aggregateって知ってる?
aggregateの定義 aggregateの定義 型Tが集成体であるための条件は以下である: 型Tが集成体であるための条件は以下である: 1.ユーザー定義されたコンストラクタ、explicitなコンストラクタ、継承 コンストラクタを持たない 2.private∕protectedな⾮静的メンバ変数を持たない 3.仮想関数を持たない 4.仮想基本クラス、private∕protected基本クラスを持たない
aggregateだと aggregateだと 集成体初期化できる 集成体初期化できる structPoint{ intx,y; }; Pointa={1,2};//aggregate-initialization
集成体初期化の罠 集成体初期化の罠 **ユーザー定義された(uesr-provided)**コンストラクタ、explicitなコ ンストラクタ、継承コンストラクタを持たない ...定義? structWTF{ WTF()=delete;//定義ではない }; WTFa={};//well-formedinC++11/14/17
集成体初期化の罠 集成体初期化の罠 C++20 C++20 ユーザー定義されたコンストラクタ、explicitなコンストラクタ、継承コ ンストラクタを持たない ユーザ宣⾔されたコンストラクタまたは継承されたコンストラクタを持 たない。 structPoint{ WTF()=delete;//わたくし、宣⾔です
}; WTFa={};//ill-formedinC++20
designatedinitialization designatedinitialization C++20 C++20 GNU拡張ではしれっと使えていた。 structPoint{ intx,y,z; }; //C++20feature:designatedinitialization Pointa={.x=1,.y=2,.z=0};
std::bool_constant std::bool_constant namespacestd{ template<boolB> usingbool_constant=integral_constant<bool,B>; }
std::bool_constant std::bool_constant example example いい例が思いつかないが・・・ クラスを場合分けで2つ書いて、std::ture_typeとstd::falseを別々に継承す るの⾯倒なので、直接条件を羅列したりするのに使えそう。 (ほんまに使うか?) template<class...Pred> structis_hoge
:std::bool_constant<(Pred::value&&...)>{};
Logicaloperation Logicaloperation metafunctions metafunctions std::cunjunction std::disjunction std::negation std::cunjunction_v std::disjunction_v
std::negation_v
クラス名で雰囲気を察せよ クラス名で雰囲気を察せよ
クラス名で雰囲気を察せよ クラス名で雰囲気を察せよ わかんない⼈絶対いる わかんない⼈絶対いる
conjunction=連⾔(れんげん) conjunction=連⾔(れんげん) つまり、∧論理積 つまり、∧論理積 disjunction=選⾔(せんげん) disjunction=選⾔(せんげん) つまり、∨論理積 つまり、∨論理積 negation=否定(ひてい) negation=否定(ひてい)
つまり、¬論理否定 つまり、¬論理否定
std::conjuncion std::conjuncion B...の論理積を⾏うメタ関数。 ⼀般にはメタ述語AとB(あるいはもっとたくさん)のすべてを満たすか 判定したい場合などに使うはず。 namespacestd{ template<class...B> structconjunction; template<class...B>
inlineconstexprbool conjunction_v=conjunction<B...>::value; }
std::conjuncion std::conjuncion example example いい例が思いつかないが・・・ パラメータパックの型がすべて同じことをSFINAEしたいときとかに使え そう。 template<typenameT,typename...P> std::enable_if_t<std::conjunction_v<std::is_same<T,P>...>> func(Thead,P...tail){
//... }
std::conjuncion std::conjuncion indetail indetail テンプレート引数が空のとき、std::true_typeを基底クラスに持つ というテンプレート引数を持つとき、 がはじ めてfalseになる型を基底クラスに持つ がtrueのとき、 を基底クラスに持つ
B , ..., B 1 n bool(B : i : value) B : n−1 : value B n
std::conjuncion std::conjuncion indetail indetail std::conjuncion_vは普通にbool。 static_assert( std::conjuncion<std::integral_constant<int,3>, std::integral_constant<int,4> >::value ==4);//OK
std::conjuncion std::conjuncion Tips Tips がはじめてfalseとなるとき であるような はそもそもvalueを持たなくて良い B : i
: value j > i Bj static_assert( std::conjunction_v<std::false_type, std::nullptr_t> ==false);//OK
std::disjuncion std::disjuncion B...の論理和を⾏うメタ関数。 ⼀般にはメタ述語AとB(あるいはもっとたくさん)のどれかを満たすか 判定したい場合などに使うはず。 namespacestd{ template<class...B> structdisjuncion; template<class...B>
inlineconstexprbool disjuncion_v=disjuncion<B...>::value; }
std::disjunction std::disjunction example example いい例が思いつかない、つらい・・・ 型Tのデフォルト値をなんやかんやする例。 template<classT> inlinestd::enable_if_t<std::disjunction_v< std::is_default_constructible<T>, std::is_aggregate<T>>,T>
default_v=[]{ ifconstexpr(std::is_aggregate_v<T>){returnT{};} else{returnT();} }();
std::disjunction std::disjunction indetail indetail テンプレート引数が空のとき、std::false_typeを基底クラスに持つ というテンプレート引数を持つとき、 がはじ めてtrueになる型を基底クラスに持つ がfalseのとき、 を基底クラスに持つ
B , ..., B 1 n bool(B : i : value) B : n−1 : value B n
std::disjunction std::disjunction indetail indetail std::disjunction_vは普通にbool。 static_assert( std::disjunction<std::integral_constant<int,4>, std::integral_constant<int,3> >::value==4);//OK
std::disjunction std::disjunction Tips Tips がはじめてtrueとなるとき であるような はそもそもvalueを持たなくて良い B : i
: value j > i Bj static_assert( std::disjunction_v<std::true_type, std::nullptr_t> ==true);//OK
std::negation std::negation あるメタ述語Bの論理否定を⾏うメタ関数。 std::bool_constant<!bool(B::value)>を基底クラスに持つ。 メタ述語の否定のメタ述語を作りたいときに。 namespacestd{ template<classB> structnegation:bool_constant<!bool(B::value)>{}; template<classB>
inlineconstexprbool negation_v=negation<B...>::value; }
std::negation std::negation example example std::conjunction,std::disjunctionのパラメータパック内でメタ述語 の否定をしたいときに使うのは想定されるよね。 メタ述語でなくいいなら!std::is_integral_v<Ts>...と書ける。 template<typename...Ts> std::enable_if_t<std::conjunction_v< std::negation<std::is_integral<Ts>>...>>
func(Ts...xs){//Ts...はすべて整数ではない型 //... }
TraitsforSFINAE-friendly TraitsforSFINAE-friendly swap swap 多次元配列のswapがnoexcept(false)になるCoreIssueの修正のために 提案された
引数が多次元配列のとき(つまりTが配列のとき)、swapがADLの候補 に上がらないのがおわかりだろうか? template<classT,size_tN> voidswap(T(&a)[N],T(&b)[N]) noexcept(noexcept(swap(*a,*b)));
TraitsforSFINAE-friendlyswapの提案によて修正。 template<classT,size_tN> voidswap(T(&a)[N],T(&b)[N]) noexcept(is_nothrow_swappable_v<T>);
is_swappableの⼀族 is_swappableの⼀族 規格書のTable42—Typepropertypredicatesでひときわ⻑い説明が書い てあるので印象深い。 template<classT,classU> structis_swappable_with; template<classT> structis_swappable;
template<classT,classU> structis_nothrow_swappable_with; template<classT> structis_nothrow_swappable;
is_swappable_with<T,U> is_swappable_with<T,U> usingstd::swap usingstd::swapされたという条件下かつ、 されたという条件下かつ、 未評価な⽂脈で 未評価な⽂脈で swap(declval<T>(),declval<U>()) swap(declval<T>(),declval<U>())と と
swap(declval<U>(),declval<T>()) swap(declval<U>(),declval<T>())が が ともにwell-formedな式となるかを判定する。 ともにwell-formedな式となるかを判定する。 Preconditions 型TとUが、完全型であること。 もしくは(cv修飾された)voidか、要素数不明の配列型であること。
§23.15.4.3Type §23.15.4.3Type properties/C++17 properties/C++17 inTable42—Typepropertypredicates inTable42—Typepropertypredicates AccesscheckingisperformedasifinacontextunrelatedtoTandU. Onlythevalidityoftheimmediatecontextoftheswapexpressionsis considered. [Note:Thecompilationoftheexpressionscanresultinsideeffectssuch
astheinstantiationofclasstemplatespecializationsandfunction templatespecializations,thegenerationofimplicitly-definedfunctions, andsoon.Suchsideeffectsarenotinthe immediatecontext andcan resultintheprogrambeingill-formed.—endnote]
cpprefjpより引⽤ cpprefjpより引⽤ このメタ関数はTとUについてのswap関数の直接のコンテキストの妥当 性(そのシグネチャで有効なswapがあるかどうか)のみをチェックす る。 そのため、結果がtrueとなったとしてもswap関数の呼び出しができるこ とは保証されない(その他の要因によりコンパイルエラーが発⽣する可 能性がある)。
別の⾔い⽅でもう⼀度 別の⾔い⽅でもう⼀度 シグネチャだけ⾒ると呼び出し可能だけども、(実際には)TやUの使⽤ によって、なんらかのテンプレートの実体化等やら暗黙に定義されたの 特殊メンバの⽣成なんやらをもろもろ起こした結果、それがなんらかの エラーとなるかもしれない。 そのような場合でもis_swappable_withはしっかりと実体化し、valueメ ンバ変数がtrueとなるかもしれないよ。
is_swappable<T> is_swappable<T> Tが参照可能な型であれば、valueメンバ変数は std::is_swappable_with<T&,T&>::valueと同じ。 それ以外であればfalse。 Preconditions 型Tが完全型であること、 もしくは(cv修飾された)voidか、要素数不明の配列型であること。
is_nothrow_swappable<T> is_nothrow_swappable<T> is_nothrow_swappable<T> is_nothrow_swappable<T> 無例外保証検査がついてお得! 無例外保証検査がついてお得!
SFINAEとは SFINAEとは テンプレートの実体化に失敗しても即座にエラーとならず、他の実体化 を探しに⾏く仕様のこと。
SFINAE-friendlyとは SFINAE-friendlyとは テンプレートの実体化の際にハードエラーを起こさないように設計され ているという意味。
SFINAE-friendlyでないメタ関数 SFINAE-friendlyでないメタ関数 #include<utility> template<typenameT1,typenameT2> structPlusResultT{ usingType =decltype(std::declval<T1>() +std::declval<T2>()); };
template<typenameT1,typenameT2> usingPlusResult =typenamePlusResultT<T1,T2>::Type;
SFINAE-friendlyなメタ関数 SFINAE-friendlyなメタ関数 #include<utility> //primarytemplate: template<typename,typename, typename=std::void_t<>> structHasPlusT:std::false_type {}; //partialspecialization(maybeSFINAE
daway): template<typenameT1,typenameT2> structHasPlusT<T1,T2, std::void_t<decltype(std::declval<T1>() +std::declval<T2>())>> :std::true_type {};
SFINAE-friendlyなメタ関数 SFINAE-friendlyなメタ関数 #include<utility> //primarytemplate, //usedwhenHasPlusTyieldstrue template<typenameT1,typenameT2, bool=HasPlusT<T1,T2>::value> structPlusResultT{ usingType=decltype(std::declval<T1>() +std::declval<T2>());
}; //partialspecialization,usedotherwise template<typenameT1,typenameT2> structPlusResultT<T1,T2,false>{};
SFINAE-friendlyであるために SFINAE-friendlyであるために コンテキストが正しいかのみを確認する 結果の型を取りたい場合は先にコンテキストが正しいかを確認する そして致命的なエラーが起こる場合を特殊化する必要がある
参考⽂献 参考⽂献 ISO/IEC148822017 C++Templates:TheCompleteGuide-SecondEdition workingdraft http://eel.is/c++draft/dcl.init.aggr Adding[nothrow-]swappabletraits,revision3 http://www.open- std.org/jtc1/sc22/wg21/docs/papers/2016/p0185r1.html Stackoverlow:Whyisswappingmultidimensionalarraysnotnoexcept?
https://stackoverflow.com/questions/26793979/why-is-swapping- multidimensional-arrays-not-noexcept