C++コンパイル時の型をチェックできるTypeDisplayer

TypeDisplayerというものをたまたま見かけて、割と使えそうだったので試してみました。

TypeDisplayerとは以下のようなコードです。

template<typename... T>
struct TypeDisplayer;

一見ただのプライマリテンプレートですが、別に特殊化も何もせずこのまま使います。

int main()
{
    TypeDisplayer<std::vector<std::string>::iterator> td;
}

このコードをたとえばclangでコンパイルしてみると、当然エラーが出ます。

prog.cc:10:55: error: implicit instantiation of undefined template 'TypeDisplayer<std::__1::__wrap_iter<std::__1::basic_string<char> *> >'
    TypeDisplayer<std::vector<std::string>::iterator> td;
                                                      ^
prog.cc:6:8: note: template is declared here
struct TypeDisplayer;
       ^
1 error generated.

1

Finish

一行目でご丁寧にエラーの起きている個所を教えてくれていますが、よく見てみると
TypeDisplayer<std::__1::__wrap_iter<std::__1::basic_string<char> *>>
と普段はなんだかよく分からないけどイテレータであることだけは確かなstd::vector<T = std::string>::iteratorの型を見ることが出来ています。
何が起きているかというと、TypeDisplayerは定義を持たない不完全型なので、不完全型を実体化しようとしたことによってコンパイルエラーが発生しているだけです。ただ、その際にコンパイラさんが優しく、どこで、どうして、エラーが起きているのかを教えてくれる際に、テンプレートパラメータに代入されている型を表示してくれるのです。なんて親切。

このイテレータ型を表示するだけならほぼ無意味ですが、自作メタ関数とかのテンプレートごりごりのコードをデバッグするときには役に立つでしょう。ただし、エラーを引き起こすものなので、状況次第ではとてつもなく長いエラーメッセージが吐き出される可能性があります。

また、次のように関数テンプレートの型推論を利用して、任意の変数の型をチェックすることもできます。

template<typename... T>
void TD(T...){
    TypeDisplayer<T...> td;
}

int main()
{
    auto n = 1;
    TD(n);//たぶんintと出てくるはず
}

先ほどはclangでしか試してませんでしたが、他のコンパイラではどのように表示されるのでしょうか?クロスコンパイラに使えるとなお一層役立ちますので、次のようなコードをclang,GCC,MSVCでそれぞれコンパイルして試してみました(clang,GCCはwandboxをお借りしました)。

template<typename... T>
struct TypeDisplayer;

template<typename... T>
void TD(T...){
    TypeDisplayer<T...> td;
}

int main()
{
    auto n = 1;
    auto d = 0.0001;
    auto f = [](int x, int y){return x + y;};
    TD(n, d, f);
}

clang 8.0.0

prog.cc:16:33: error: implicit instantiation of undefined template 'TypeDisplayer<int, double, (lambda at prog.cc:23:14)>'
            TypeDisplayer<T...> td;
                                ^
prog.cc:24:5: note: in instantiation of function template specialization 'TD<int, double, (lambda at prog.cc:23:14)>' requested here
    TD(n, d, f);
    ^
prog.cc:12:16: note: template is declared here
        struct TypeDisplayer;
               ^
1 error generated.

gcc 9.0.0 20180827

prog.cc: In instantiation of 'void TD(T ...) [with T = {int, double, main()::<lambda(int, int)>}]':
prog.cc:24:15:   required from here
prog.cc:16:33: error: 'TypeDisplayer<int, double, main()::<lambda(int, int)> > td' has incomplete type
16 |             TypeDisplayer<T...> td;
   |    

MSVC(VS2017 15.8.2)

1>d:\~\main.cpp(9): error C2079: 'td' が 未定義の struct 'TypeDisplayer<int,double,main::<lambda_a5eb4446fdbf7d86fd2b3289a3092fed>>' で使用しています。
1>d:\~\main.cpp(18): note: コンパイル対象の関数 テンプレート インスタンス化 'void TD<int,double,main::<lambda_a5eb4446fdbf7d86fd2b3289a3092fed>>(int,double,main::<lambda_a5eb4446fdbf7d86fd2b3289a3092fed>)' のリファレンスを確認してください

MSVC(VS2015 update3)

1>d:\~\main.cpp(11): error C2079: 'td' が 未定義の struct 'TypeDisplayer<int,double,main::<lambda_77d53e58a37eb245ae4bb57004cefa4a>>' で使用しています。
1>  d:\~\main.cpp(19): note: コンパイル対象の関数 テンプレート インスタンス化 'void TD<int,double,main::<lambda_77d53e58a37eb245ae4bb57004cefa4a>>(int,double,main::<lambda_77d53e58a37eb245ae4bb57004cefa4a>)' のリファレンスを確認してください

こうしてみると、GCCが一番ぱっと見た時に見やすいのかなあ。どれも一行目に確実に表示されているので見づらいという事はないですが。ちなみに、VS2017の場合はintellisenseの段階で赤い波線エラーが出ているので、ポップアップで確認することもできます(ただし、出るのはTD関数の変数宣言部分なので場合によっては遠いかもしれない)。

しかし、どうやら現在の主要なC++コンパイラならばTypeDisplayerを活用できそうな事が分かりました。良いテンプレートライフを満喫しましょう!

参考文献:
SpECTRE: TypeDisplayer<... > Struct Template Reference