標準 C++ で変数の型を印刷することは可能ですか? 質問する

標準 C++ で変数の型を印刷することは可能ですか? 質問する

例えば:

int a = 12;
cout << typeof(a) << endl;

期待される出力:

int

ベストアンサー1

非常に古い質問に対する 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 修飾子、参照、および lvalue/rvalue 性を破棄します。例:

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ソリューション

__cxa_demangle私はMSVC以外のプラットフォームで推奨されているように使用していますイパパドプ型をデマングルするための回答です。しかし、MSVC ではtypeid名前をデマングルすることに頼っています (テストされていません)。そして、このコアは、CV 修飾子と入力型への参照を検出、復元、報告する簡単なテストでラップされています。

#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の「型」です。(式が参照型になることはありませんが、慣例によりdecltypelvalue 式は lvalue 参照で表されます)。

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 修飾子が削除されます。

C++14 アップデート

問題の解決策が見つかったと思った瞬間に、誰かがどこからともなく現れて、もっと良い方法を教えてくれます。:-)

この答えからジャンボリーコンパイル時に C++14 で型名を取得する方法を示します。これは、いくつかの理由から優れたソリューションです。

  1. コンパイル時です!
  2. ライブラリ (std::lib であっても) の代わりにコンパイラ自体に作業を実行させます。これは、最新の言語機能 (ラムダなど) に対してより正確な結果を意味します。

ジャンボリーの 答え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 アップデート

下のコメント欄にリベルタstd::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 の定数を更新できました。

アップデート:

ぜひチェックしてくださいこれ書き直すかこれ以下に書き直すと、最新の定式化で判読できないマジックナンバーが削除されます。

おすすめ記事