例えば:
int a = 12;
cout << typeof(a) << endl;
期待される出力:
int
非常に古い質問に対する C++11 の更新: C++ で変数の型を出力します。
受け入れられた (そして良い) 答えは、 を使用することです。typeid(a).name()
ここa
で、 は変数名です。
C++11 ではdecltype(x)
、式を型に変換できる があります。そしてdecltype()
、非常に興味深いルールの独自のセットが付属しています。たとえば、decltype(a)
とdecltype((a))
は一般的に異なるタイプになります (そして、それらの理由が明らかになると、正当で理解できる理由があります)。
私たちの信頼は、typeid(a).name()
この素晴らしい新しい世界を探検するのに役立ちますか?
いいえ。
しかし、そのツールはそれほど複雑ではありません。そして、この質問に対する答えとして私が使用しているのは、そのツールです。この新しいツールと を比較対照しtypeid(a).name()
ます。そして、この新しいツールは、実際には の上に構築されていtypeid(a).name()
ます。
根本的な問題:
typeid(a).name()
cv 修飾子、参照、および左辺値/右辺値性を破棄します。例えば:
const int ci = 0;
std::cout << typeid(ci).name() << '\n';
私にとっては出力:
i
そして、私はMSVC出力を推測しています:
int
つまり、const
なくなっています。これは QOI (実装の品質) の問題ではありません。標準では、この動作が義務付けられています。
私がお勧めするのは以下です。
template <typename T> std::string type_name();
これは次のように使用されます。
const int ci = 0;
std::cout << type_name<decltype(ci)>() << '\n';
そして私にとっては出力:
int const
<disclaimer>
MSVC でこれをテストしていません。</disclaimer>
しかし、そうしている人々からのフィードバックを歓迎します。
C++11 ソリューション
demangle タイプに対する彼の回答でipapadopが推奨するように、私は__cxa_demangle
非 MSVC プラットフォームに使用しています。しかし、MSVC では、名前をデマングルすることを信頼しています (未テスト)。そして、このコアは、cv 修飾子と入力型への参照を検出、復元、および報告するいくつかの簡単なテストにラップされています。typeid
#include <type_traits>
#include <typeinfo>
#ifndef _MSC_VER
# include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>
template <class T>
std::string
type_name()
{
typedef typename std::remove_reference<T>::type TR;
std::unique_ptr<char, void(*)(void*)> own
(
#ifndef _MSC_VER
abi::__cxa_demangle(typeid(TR).name(), nullptr,
nullptr, nullptr),
#else
nullptr,
#endif
std::free
);
std::string r = own != nullptr ? own.get() : typeid(TR).name();
if (std::is_const<TR>::value)
r += " const";
if (std::is_volatile<TR>::value)
r += " volatile";
if (std::is_lvalue_reference<T>::value)
r += "&";
else if (std::is_rvalue_reference<T>::value)
r += "&&";
return r;
}
結果
このソリューションを使用すると、次のことができます。
int& foo_lref();
int&& foo_rref();
int foo_value();
int
main()
{
int i = 0;
const int ci = 0;
std::cout << "decltype(i) is " << type_name<decltype(i)>() << '\n';
std::cout << "decltype((i)) is " << type_name<decltype((i))>() << '\n';
std::cout << "decltype(ci) is " << type_name<decltype(ci)>() << '\n';
std::cout << "decltype((ci)) is " << type_name<decltype((ci))>() << '\n';
std::cout << "decltype(static_cast<int&>(i)) is " << type_name<decltype(static_cast<int&>(i))>() << '\n';
std::cout << "decltype(static_cast<int&&>(i)) is " << type_name<decltype(static_cast<int&&>(i))>() << '\n';
std::cout << "decltype(static_cast<int>(i)) is " << type_name<decltype(static_cast<int>(i))>() << '\n';
std::cout << "decltype(foo_lref()) is " << type_name<decltype(foo_lref())>() << '\n';
std::cout << "decltype(foo_rref()) is " << type_name<decltype(foo_rref())>() << '\n';
std::cout << "decltype(foo_value()) is " << type_name<decltype(foo_value())>() << '\n';
}
出力は次のとおりです。
decltype(i) is int
decltype((i)) is int&
decltype(ci) is int const
decltype((ci)) is int const&
decltype(static_cast<int&>(i)) is int&
decltype(static_cast<int&&>(i)) is int&&
decltype(static_cast<int>(i)) is int
decltype(foo_lref()) is int&
decltype(foo_rref()) is int&&
decltype(foo_value()) is int
(たとえば) と の違いに注意してdecltype(i)
くださいdecltype((i))
。前者は の宣言の型ですi
。後者は式の「型」です i
。(式には参照型はありませんが、慣例として、decltype
左辺値参照を持つ左辺値式を表します)。
decltype
したがって、このツールは、独自のコードを調べてデバッグするだけでなく、 について学習するための優れた手段です。
対照的に、typeid(a).name()
失われた cv 修飾子や参照を追加せずに だけでこれをビルドすると、出力は次のようになります。
decltype(i) is int
decltype((i)) is int
decltype(ci) is int
decltype((ci)) is int
decltype(static_cast<int&>(i)) is int
decltype(static_cast<int&&>(i)) is int
decltype(static_cast<int>(i)) is int
decltype(foo_lref()) is int
decltype(foo_rref()) is int
decltype(foo_value()) is int
つまり、すべての参照と cv-qualifier が取り除かれます。
C++14 アップデート
問題の解決策を見つけたと思ったとき、誰かがどこからともなく現れて、より良い方法を示してくれます。:-)
Jamboreeからのこの回答は、コンパイル時に C++14 で型名を取得する方法を示しています。これは、いくつかの理由から優れたソリューションです。
Jamboree の 回答は、VS のすべてを完全にレイアウトしているわけではありません。私は彼のコードを少し調整しています。しかし、この回答は多くのビューを獲得しているため、時間をかけてそこに行き、彼の回答に賛成票を投じてください。それがなければ、この更新は行われませんでした.
#include <cstddef>
#include <stdexcept>
#include <cstring>
#include <ostream>
#ifndef _MSC_VER
# if __cplusplus < 201103
# define CONSTEXPR11_TN
# define CONSTEXPR14_TN
# define NOEXCEPT_TN
# elif __cplusplus < 201402
# define CONSTEXPR11_TN constexpr
# define CONSTEXPR14_TN
# define NOEXCEPT_TN noexcept
# else
# define CONSTEXPR11_TN constexpr
# define CONSTEXPR14_TN constexpr
# define NOEXCEPT_TN noexcept
# endif
#else // _MSC_VER
# if _MSC_VER < 1900
# define CONSTEXPR11_TN
# define CONSTEXPR14_TN
# define NOEXCEPT_TN
# elif _MSC_VER < 2000
# define CONSTEXPR11_TN constexpr
# define CONSTEXPR14_TN
# define NOEXCEPT_TN noexcept
# else
# define CONSTEXPR11_TN constexpr
# define CONSTEXPR14_TN constexpr
# define NOEXCEPT_TN noexcept
# endif
#endif // _MSC_VER
class static_string
{
const char* const p_;
const std::size_t sz_;
public:
typedef const char* const_iterator;
template <std::size_t N>
CONSTEXPR11_TN static_string(const char(&a)[N]) NOEXCEPT_TN
: p_(a)
, sz_(N-1)
{}
CONSTEXPR11_TN static_string(const char* p, std::size_t N) NOEXCEPT_TN
: p_(p)
, sz_(N)
{}
CONSTEXPR11_TN const char* data() const NOEXCEPT_TN {return p_;}
CONSTEXPR11_TN std::size_t size() const NOEXCEPT_TN {return sz_;}
CONSTEXPR11_TN const_iterator begin() const NOEXCEPT_TN {return p_;}
CONSTEXPR11_TN const_iterator end() const NOEXCEPT_TN {return p_ + sz_;}
CONSTEXPR11_TN char operator[](std::size_t n) const
{
return n < sz_ ? p_[n] : throw std::out_of_range("static_string");
}
};
inline
std::ostream&
operator<<(std::ostream& os, static_string const& s)
{
return os.write(s.data(), s.size());
}
template <class T>
CONSTEXPR14_TN
static_string
type_name()
{
#ifdef __clang__
static_string p = __PRETTY_FUNCTION__;
return static_string(p.data() + 31, p.size() - 31 - 1);
#elif defined(__GNUC__)
static_string p = __PRETTY_FUNCTION__;
# if __cplusplus < 201402
return static_string(p.data() + 36, p.size() - 36 - 1);
# else
return static_string(p.data() + 46, p.size() - 46 - 1);
# endif
#elif defined(_MSC_VER)
static_string p = __FUNCSIG__;
return static_string(p.data() + 38, p.size() - 38 - 7);
#endif
}
このコードはconstexpr
、古い C++11 にまだ行き詰まっている場合に自動的にバックオフします。また、C++98/03 で洞窟の壁にペイントしている場合は、noexcept
も犠牲になります。
C++17 アップデート
以下のコメントで、 Lybertastd::string_view
は、新しいものは次のように置き換えることができると指摘していstatic_string
ます。
template <class T>
constexpr
std::string_view
type_name()
{
using namespace std;
#ifdef __clang__
string_view p = __PRETTY_FUNCTION__;
return string_view(p.data() + 34, p.size() - 34 - 1);
#elif defined(__GNUC__)
string_view p = __PRETTY_FUNCTION__;
# if __cplusplus < 201402
return string_view(p.data() + 36, p.size() - 36 - 1);
# else
return string_view(p.data() + 49, p.find(';', 49) - 49);
# endif
#elif defined(_MSC_VER)
string_view p = __FUNCSIG__;
return string_view(p.data() + 84, p.size() - 84 - 7);
#endif
}
以下のコメントで、Jive Dadson による非常に優れた調査作業のおかげで、VS の定数を更新しました。
私の最新の定式化で判読不能なマジックナンバーを排除する以下の書き換えを必ずチェックしてください.
試す:
#include <typeinfo>
// …
std::cout << typeid(a).name() << '\n';
これを機能させるには、コンパイラ オプションで RTTI を有効にする必要がある場合があります。さらに、この出力はコンパイラによって異なります。それは、生の型名または名前マングリング シンボル、またはその間の何かである可能性があります。
非常に醜いですが、コンパイル時の情報のみが必要な場合 (デバッグ用など) にはうまくいきます:
auto testVar = std::make_tuple(1, 1.0, "abc");
decltype(testVar)::foo= 1;
戻り値:
Compilation finished with errors:
source.cpp: In function 'int main()':
source.cpp:5:19: error: 'foo' is not a member of 'std::tuple<int, double, const char*>'
含めることを忘れないでください<typeinfo>
あなたが言及しているのはランタイムタイプの識別だと思います。を実行することで上記を実現できます。
#include <iostream>
#include <typeinfo>
using namespace std;
int main() {
int i;
cout << typeid(i).name();
return 0;
}
C++のRTTI機能によって生成された名前は移植性がないことに注意してください。たとえば、クラス
MyNamespace::CMyContainer<int, test_MyNamespace::CMyObject>
次の名前になります。
// MSVC 2003:
class MyNamespace::CMyContainer[int,class test_MyNamespace::CMyObject]
// G++ 4.2:
N8MyNamespace8CMyContainerIiN13test_MyNamespace9CMyObjectEEE
したがって、この情報をシリアル化に使用することはできません。ただし、それでも、typeid(a).name()プロパティはログ/デバッグの目的で使用できます。
テンプレートを使用できます。
template <typename T> const char* typeof(T&) { return "unknown"; } // default
template<> const char* typeof(int&) { return "int"; }
template<> const char* typeof(float&) { return "float"; }
上記の例では、タイプが一致しない場合、「不明」と出力されます。
前述のようにtypeid().name()
、壊れた名前を返す場合があります。GCC(およびその他のコンパイラ)では、次のコードで回避できます。
#include <cxxabi.h>
#include <iostream>
#include <typeinfo>
#include <cstdlib>
namespace some_namespace { namespace another_namespace {
class my_class { };
} }
int main() {
typedef some_namespace::another_namespace::my_class my_type;
// mangled
std::cout << typeid(my_type).name() << std::endl;
// unmangled
int status = 0;
char* demangled = abi::__cxa_demangle(typeid(my_type).name(), 0, 0, &status);
switch (status) {
case -1: {
// could not allocate memory
std::cout << "Could not allocate memory" << std::endl;
return -1;
} break;
case -2: {
// invalid name under the C++ ABI mangling rules
std::cout << "Invalid name" << std::endl;
return -1;
} break;
case -3: {
// invalid argument
std::cout << "Invalid argument to demangle()" << std::endl;
return -1;
} break;
}
std::cout << demangled << std::endl;
free(demangled);
return 0;
}
これには特性クラスを使用できます。何かのようなもの:
#include <iostream>
using namespace std;
template <typename T> class type_name {
public:
static const char *name;
};
#define DECLARE_TYPE_NAME(x) template<> const char *type_name<x>::name = #x;
#define GET_TYPE_NAME(x) (type_name<typeof(x)>::name)
DECLARE_TYPE_NAME(int);
int main()
{
int a = 12;
cout << GET_TYPE_NAME(a) << endl;
}
define は、必要と予想されるすべての型に対してこの特性クラスを宣言する際のDECLARE_TYPE_NAME
作業を容易にするために存在します。
typeid
これは、出力を制御できるため、関連するソリューションよりも役立つ場合があります。たとえば、私のコンパイラtypeid
で forを使用すると、"x" が返されます。long long
オプション -t (タイプ) を指定して c++filt を使用して、タイプ名をデマングルすることもできます。
#include <iostream>
#include <typeinfo>
#include <string>
using namespace std;
int main() {
auto x = 1;
string my_type = typeid(x).name();
system(("echo " + my_type + " | c++filt -t").c_str());
return 0;
}
Linux のみでテスト済み。
RTTI(typeid)に関連する他の回答は、次の場合に限り、おそらくあなたが望むものです。
別の方法(Greg Hewgillの回答と同様)は、特性のコンパイル時テーブルを作成することです。
template <typename T> struct type_as_string;
// declare your Wibble type (probably with definition of Wibble)
template <>
struct type_as_string<Wibble>
{
static const char* const value = "Wibble";
};
宣言をマクロでラップすると、コンマが原因で、複数のパラメーター(std :: mapなど)を使用するテンプレートタイプの名前を宣言する際に問題が発生することに注意してください。
変数の型の名前にアクセスするには、必要なのは
template <typename T>
const char* get_type_as_string(const T&)
{
return type_as_string<T>::value;
}
以前のソリューションよりも関数のオーバーロードのない、より一般的なソリューション:
template<typename T>
std::string TypeOf(T){
std::string Type="unknown";
if(std::is_same<T,int>::value) Type="int";
if(std::is_same<T,std::string>::value) Type="String";
if(std::is_same<T,MyClass>::value) Type="MyClass";
return Type;}
ここで MyClass はユーザー定義クラスです。ここでさらに条件を追加することもできます。
例:
#include <iostream>
class MyClass{};
template<typename T>
std::string TypeOf(T){
std::string Type="unknown";
if(std::is_same<T,int>::value) Type="int";
if(std::is_same<T,std::string>::value) Type="String";
if(std::is_same<T,MyClass>::value) Type="MyClass";
return Type;}
int main(){;
int a=0;
std::string s="";
MyClass my;
std::cout<<TypeOf(a)<<std::endl;
std::cout<<TypeOf(s)<<std::endl;
std::cout<<TypeOf(my)<<std::endl;
return 0;}
出力:
int
String
MyClass
スコット・マイヤーズがEffective Modern C++で説明したように、
への呼び出し
std::type_info::name
は、適切なものを返すことが保証されていません。
最善の解決策は、型推定中にコンパイラにエラー メッセージを生成させることです。たとえば、
template<typename T>
class TD;
int main(){
const int theAnswer = 32;
auto x = theAnswer;
auto y = &theAnswer;
TD<decltype(x)> xType;
TD<decltype(y)> yType;
return 0;
}
コンパイラによっては、結果は次のようになります。
test4.cpp:10:21: error: aggregate ‘TD<int> xType’ has incomplete type and cannot be defined TD<decltype(x)> xType;
test4.cpp:11:21: error: aggregate ‘TD<const int *> yType’ has incomplete type and cannot be defined TD<decltype(y)> yType;
したがって、 のタイプは であり、のx
タイプはであることがわかります。int
y
const int*