読者です 読者をやめる 読者になる 読者になる

貝柱が何かをする所

貝柱が何かをする所です、C++/Perl/その他諸々

非型テンプレートパラメータに指定できる値の制限をする

C++

自分の中でHotなうちに記事に書きおろそうと思う。
タイトルは非型テンプレートパラメータに制限を、と書いているが、doubleが使えないとかそういう事ではない。
もう少し単純な話だ。なお、この話はコアなのであまり万人には役に立たないかもしれない。
あと、以下の記事はVS2013で主に発案したので本来はconstexprを使用しなければならない所が多数ある事、
また、逆にいうとconstexprが使用できない制限下で行ったことを把握願いたい。

動機

C++ではご存知の通りテンプレートという強力な機構が存在する。
通常テンプレートパラメータには型を指定するが、次のように値を指定する事ができる。

template<int V>
struct Hoge{
    static const int value = V;
};
int main(void){
    std::cout << Hoge<10>::value << std::endl;
}

上記を非型テンプレートパラメータなどという。

ところで、ここで渡すパラメータに制限を持たせることはできないだろうか。
例えば正の整数のみで、負は許可しない。 以下のようなコードを書いた時、Hoge<-1>によってコンパイルエラーになってほしい。

Hoge<10>::value;        // ok. これは許可する
Hoge<-1>::value;        // no. これは許可しない

これを実現する。

アプローチ

まず必要になるのは、こちらが望む制限を実装するメタ関数だ。
これは単純に以下のように実装できる。

//内部に定数式が正負だったかの真偽値を持つメタ関数
template<int val>
struct is_positive{
    static const bool value = (val >= 0);
};
int main(void){
    if( is_positive<10>::value ){
        std::cout << "true" << std::endl;
    }
    
    return 0;
}

メタ関数is_positiveは内部valueに、非型テンプレートパラメータが制限を満たすかのbool値を持つ。

次に目的のクラスの実装を、以下のように行う

template<bool B, int V = 0>
struct positive_struct{
    positive_struct() = delete;
};

template<int V>
struct positive_struct<true, V>{
    positive_struct() = default;
    static const int val = V;
};

上記の実装はstd::enable_ifを参考にした。
こうすることによって、positive_structは第一テンプレートパラメータの真偽値によって、内部にvalを持つ場合、あるいはインスタンス化を抑制できる。
これを先ほどのメタ関数と合わせる。

#include <iostream>

//内部に定数式が正負だったかの真偽値を持つメタ関数
template<int val>
struct is_positive{
    static const bool value = (val >= 0);
};

template<bool B, int V = 0>
struct positive_struct{
    positive_struct() = delete;
};

template<int V>
struct positive_struct<true, V>{
    positive_struct() = default;
    static const int val = V;
};

template<int V>
using positive_struct_decl = positive_struct< is_positive<V>::value, V>;

int main(void){
    std::cout << positive_struct_decl<20>::val;
//    std::cout << positive_struct_decl<-15>::val;  これは存在しないのでコンパイルエラーになる
//  positive_struct_decl<-1> hoge;    これもエラー
   
    return 0;
}

実際には宣言を容易にするためにエイリアステンプレートを使う。
この実装を使えば、非型テンプレートの値によるインスタンス化の制限もできる。
また、嬉しい副作用としてVS2013以降ならば、コードを記述しただけでインテリセンスが働き、波線で注意してくれる。
(他のC++ IDEもいけるのかもしれない)

あとがき

なかなか需要がない実装かもしれない。
実際これを使おうと思った時にはどういう目的で使おうとしたのか忘れてしまった。
だが、せっかくなので記事にしてみた。 あと、やはり記事を書くのは時間がかかってしまう。

この記事を書きながら「この美術部には問題がある!」を見ていたのだが、自分も頭の中に思い描く嫁をあのようにキャンバスに上手く表したいものだ。