初心者なので,ググったりなんだりして template なコード見かけた際に
「何この文法は? 読めないんですけど??」
ってなることが割とあるので,調べたことをなんとなくメモしようかと.
template 引数名は省略可
以下の例で template 引数の名前( T, I )は全く使われてない.
こういうのは無くて良いらしい.
template< typename T > //←この名前 T は使われてない
void F(){ std::cout << "This is F<T>()\n"; }
template< int I > //←この名前 I は使われてない
void K(){ std::cout << "This is K<int>()\n"; }
↓
template< typename > //←名前が不要なら書かなくていい
void F(){ std::cout << "This is F<T>()\n"; }
template< int > //←名前が不要なら書かなくていい
void K(){ std::cout << "This is K<int>()\n"; }
「そしたら何故 template にしたし?」というと,目的は特殊化なのだろう.
//特定の型の場合は尊い文字列を表示する
template<>
void F<int>(){ std::cout << "This is F<int>() ; Cheetoh !\n"; }
//特定の値の場合は尊い文字列を表示する
template<>
void K<7>(){ std::cout << "This is K<7>() ; Toyger !\n"; }
template 引数が template とかいう
こういうやつ↓.
見た瞬間にめまいがする.自分で書く機会は永久に無さそう.
template< template<typename> typename T >
void G(){ /* ... */ }
どうやら
-
Gは関数テンプレートであって,その template 引数は型引数が1つ(T)であり……- この
Tに使える型というのは, template 引数が型引数1個なクラステンプレートなのですぞ
- この
……っていうような話である様子.
だから G() の実装内にはきっとこんなの↓が出現することになるのだと思われ.
template< template<typename> typename T >
void G()
{
T< std::string > T_Instance; //← T はこれがコンパイル通る型
}
なお,ここで T の template 引数に名前を付けてみたところで,それを G() 内で使えるわけではない とのことだ.
// X っていう名前を書いてみたけども……
template< template<typename X> typename T >
void G()
{
//コンパイルエラーが2つ出た(VS2019)
//C2065 'X': 定義されていない識別子です
//C3861 'X': 識別子が見つかりませんでした
std::cout << typeid(X).name(); //(※なんかてきとーに X を使おうとした記述)
}
template<auto>
stack overflow を何となく眺めてたら,唐突な auto が出てきた.
まぁ使う機会は無さそう.
//こんなものを書く場合……
template< typename T, T Val > //「非型パラメタValの型 を 型パラメタにしたい」とかいう
void F(){ std::cout << Val << std::endl; }
F<int,7>(); //使用例
F<char,'c'>(); //使用例
/* --- ↓ --- */
//こう書ける,という話みたい
template< auto Val >
void F(){ std::cout << Val << std::endl; }
F<7>(); //使用例
F<'c'>(); //使用例
試行MEMO :
//名称の省略と特殊化
template< auto > void F2(){ std::cout << "Cat\n"; }
template<> void F2<100>(){ std::cout << "Wild Cat!!\n"; }
//Fold とかいうのと組み合わせてみる
template< auto ...Vals >
void F3()
{ ( (std::cout << Vals << " ") , ... ); }
F3<'c', 40, -7, '\n'>(); //使用例
多重継承
こんなのを見かけた.
//なんかこんなのがあるとして……
template< typename T > class Base { /*略*/ };
//こんな書き方ができるらしい
template< typename ...Ts >
struct Deriv : Base<Ts>...
{ /*略*/ };
Deriv<X,Y> は Base<X> と Base<Y> を継承する,という形か.
多重継承を variadic template でこう書けるという話らしい:
template< typename ...BaseClassTypes >
class Deriv : public BaseClasTypes...
{ /*略*/ };
Deriv<X,Y> は X と Y を継承する,と.
推論ガイド(?)
template と -> が組み合わさった謎記述.
( std::visit について調べてたときに遭遇したハズ.↑の多重継承みたいな記述を用いて複数のラムダを継承することで様々な引数型の operator() を持つ class を用意するよ!……みたいな話だったような記憶.あやふや)
多分これだろう,という話が cpprefjp にあるが,内容未把握.
CRTP
説明をわりとよく見かける気がする「奇妙に再起してどうの~」とかいうやつ.
「いつ何に使うんだよ?」っていうと,「Template Method パターンを静的にやれるよ」みたいなことらしい.多分.
//基底クラス:派生クラスの型をテンプレート型引数とする
template< class Deriv_t >
class Base
{
public:
void ProcA()
{//{A1,A2,A3}の順で3つの作業をするけど,A2の具体的な実装については派生型に任せるぜ!
std::cout << "A1 -> ";
Deriv().ProcA2( std::cout );
std::cout << " -> A3\n";
}
void ProcB() const
{//(constメンバ関数版)
std::cout << "B1 -> ";
Deriv().ProcB2( std::cout );
std::cout << " -> B3\n";
}
private: //Deriv_t という型が指定されてるんだから,コレができるよな? という話
Deriv_t &Deriv(){ return *static_cast<Deriv_t*>( this ); }
const Deriv_t &Deriv() const { return *static_cast<const Deriv_t*>( this ); }
};
//派生クラスの実装
class Test : public Base<Test>
{
friend class Base<Test>; //(↓のメンバ関数群をprivateにしたいので)
private:
void ProcA2( std::ostream &OStrm ){ OStrm << "Test_A2"; }
void ProcB2( std::ostream &OStrm ) const { OStrm << "Test_B2"; }
};
それはそれとして,「これ,ProcA2() とかの仕様はどこに書いておけばいいの?」とかそういうのが気になる.
(動的多態の場合なら基底クラス側に仮想関数を書くからそこにコメントで書くんだけど)
複数のパラメータパックが欲しいんですけど?
こういう場合↓,DOしたらE?
//F1()とF2()の引数群をパラメータパックにしたいです!
//でも,こんなのは書けないので……
template< typename...Xs, typename ...Ys>
class Z
{
public:
void F1( Xs ...args ){ ( ( std::cout << args << " " ), ... ); std::cout << " (F1)\n"; }
void F2( Ys ...args ){ ( ( std::cout << args << " " ), ... ); std::cout << " (F2)\n"; }
};
なんかこんな感じ↓にして頑張るらしい.
//とりあえずこんなのを書いておいて
template< class X, class Y >
class Z{};
//↑をこんな感じで特殊化(?)する形で目的の実装を行う……らしい
template< typename...Xs, typename ...Ys>
class Z< std::tuple<Xs...>, std::tuple<Ys...> >
{
public:
void F1( Xs ...args ){ ( ( std::cout << args << " " ), ... ); std::cout << " (F1)\n"; }
void F2( Ys ...args ){ ( ( std::cout << args << " " ), ... ); std::cout << " (F2)\n"; }
};
//--- Test ---
//(適当に別名を付けないとやってられない感)
using MyZ = Z< std::tuple<>, std::tuple<const std::string&,double> >;
int main()
{
MyZ z;
z.F1( );
z.F2( "Hello", 3.14 );
}
畳み込み式 (fold expression)
( パラメータパックの名前を用いた処理記述 演算子 ...); みたいな記述.
演算子の部分にはカンマを使う場面が多そう.
//例:スペース区切りで出力するよ
template< class Head, class ...vals >
void F( Head head, vals ...v )
{
std::cout << head;
( (std::cout << " " << v) , ... );
}
エイリアステンプレート(using) を部分特殊化したい
以下のような場面で困った:
//こういうのがあって……
template< class Ret, class Arg >
using Func = std::function< Ret(const Arg&) >;
//↑の「引数無し」な場合を用意したいのだが?
//(失敗1)同じ名前はダメだと言われる
template< class Ret >
using Func = std::function< Ret() >;
//(失敗2)エイリアステンプレートは特殊化できないと言われる
template< class Ret >
using Func<Ret,void> = std::function< Ret() >;
struct に包めばいけた.
//エイリアステンプレートを struct の中に入れれば……
template< class Ret, class Arg >
struct Func{ using f = std::function<Ret(const Arg&)>; };
//struct を部分特殊化できる
template< class Ret >
struct Func< Ret, void >{ using f = std::function<Ret()>; };
//さらにこういうのを書いて使えばおk
template< class Ret, class Arg >
using Func_t = typename Func<Ret,Arg>::f;
arrayのサイズを書くのが面倒
(ちょっと他とは毛色が違う話だけど)
std::array<int,3> A = { 10, 20, 30 }; みたいなやつの 3 が面倒すぎると思っていた.
= の右側の変更に合わせていちいち正当な値に修正しなきゃならない(要素が増えた際はコンパイルエラーになるが減った場合だとそうもいかないし)
が, C++20 だと std::to_array とかいうやつを使って
auto A = std::to_array<int>( { 10, 20, 30 } ); と書けるとのことだ.
( int のところは省略しない方が無難かな )
以降未定
(今後,何か増えたら追記しようかと)