yohhoyの日記

技術的メモをしていきたい日記

typeid演算子と参照型/cv修飾

プログラミング言語C++の typeid 演算子における、参照型および cv修飾(const, volatile)の扱いについてメモ。

typeid演算子オペランドに型名(type-id)を指定する場合、下記ルールが適用されることに注意。

  • 対象が参照型(T&, T&&)の場合、参照先の型Tとして扱う。
  • トップレベルの const/volatile 修飾は無視する。

メタ関数のデバッグ時など、関数テンプレートfのテンプレートパラメータTがどの型に推論されたかを確認したいケース(注意:例ではabi::__cxa_demangle戻り値を解放せずメモリリークするため、デバッグ用途に留めること*1。):

#include <iostream>
#include <typeinfo>
#include <cxxabi.h> // __cxa_demangle

struct A {};  // 適当な型A

template <class T>
void f(T&&)
{
  // typeid( T ) による確認
  std::cout << abi::__cxa_demangle(typeid(T).name(),0,0,0) << std::endl;
}

               A g1() { return {}; }
const          A g2() { return {}; }
      volatile A g3() { return {}; }
const volatile A g4() { return {}; }

int main()
{
                 A a = {};
  const          A ca = {};
        volatile A va = {};
  const volatile A cva = {};

  f(A{});
  f(a); f(ca); f(va); f(cva);
  f(g1()); f(g2()); f(g3()); f(g4());
}

上記コードでは全パターンで単に 型A と出力されてしまい*2、推論されたテンプレートパラメータTの正確な型を識別できない。これを回避するには型Tを適当なクラステンプレートXに指定し、前掲のtypeid演算子に関するルール適用外とすればよい。

template <class> struct X {};

template <class T>
void f(T&&)
{
  // typeid( X<T> ) による確認
  std::cout << abi::__cxa_demangle(typeid(X<T>).name(),0,0,0) << std::endl;
}

gcc 4.6.3での実行結果:

X<A>
X<A&>
X<A const&>
X<A volatile&>
X<A const volatile&>
X<A>
X<A const>
X<A volatile>
X<A const volatile>

C++03 5.2.8/p4-5より引用。

4 When typeid is applied to a type-id, the result refers to a type_info object representing the type of the type-id. If the type of the type-id is a reference type, the result of the typeid expression refers to a type_info object representing the referenced type. If the type of the type-id is a class type or a reference to a class type, the class shall be completely-defined. Types shall not be defined in the type-id.
5 The top-level cv-qualifiers of the lvalue expression or the type-id that is the operand of typeid are always ignored. [Example:

class D { ... };
D d1;
const D d2;
typeid(d1) == typeid(d2);      // yields true
typeid(D) == typeid(const D);  // yields true
typeid(D) == typeid(d2);       // yields true
typeid(D) == typeid(const D&); // yields true

-- end example]

*1:__cxa_demangle は gcc/libstdc++ の名前デマングル関数。C++ のシンボルをデマングルする - bkブログ参照

*2:type_info::name メンバ関数の戻り値は “処理系定義の文字列(implementation-defined NTBS)” とのみ定義されるため、C++コンパイラによって出力文字列は異なる。(C++03 18.5.1/p7, C++11(N3337) 18.7.1/p9)