629 Stimmen

Ist es möglich, den Typ einer Variablen in Standard-C++ zu drucken?

Zum Beispiel:

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

Erwartetes Ergebnis:

int

3 Stimmen

Hier ist eine Zusammenfassung von Howards Langformlösung, die jedoch mit einem ketzerischen einzeiligen Makro umgesetzt wurde: #define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL) . Wenn Sie plattformübergreifende Unterstützung benötigen: Verwenden Sie . #ifdef , #else , #endif um ein Makro für andere Plattformen wie MSVC bereitzustellen.

0 Stimmen

Mit expliziteren, für Menschen lesbaren Anforderungen: stackoverflow.com/questions/12877521/

3 Stimmen

Wenn Sie dies nur zum Debuggen verwenden, sollten Sie Folgendes in Betracht ziehen template<typename T> void print_T() { std::cout << __PRETTY_FUNCTION__ << '\n'; } . Dann verwenden Sie z.B. print_T<const int * const **>(); wird gedruckt void print_T() [T = const int *const **] zur Laufzeit und bewahrt alle Qualifier (funktioniert in GCC und Clang).

794voto

Howard Hinnant Punkte 191035

C++11 Update zu einer sehr alten Frage: Variablentyp in C++ drucken.

Die akzeptierte (und gute) Antwort ist die Verwendung von typeid(a).name() , donde a ist ein Variablenname.

In C++11 haben wir jetzt decltype(x) die einen Ausdruck in einen Typ umwandeln kann. Und decltype() ist mit einer Reihe sehr interessanter Regeln verbunden. Zum Beispiel decltype(a) y decltype((a)) werden in der Regel unterschiedliche Typen sein (und zwar aus guten und verständlichen Gründen, wenn diese Gründe bekannt sind).

Wird unser treuer typeid(a).name() uns helfen, diese schöne neue Welt zu erkunden?

No.

Aber das Werkzeug dafür ist gar nicht so kompliziert. Und es ist dieses Werkzeug, das ich als Antwort auf diese Frage verwende. Ich werde dieses neue Werkzeug vergleichen und gegenüberstellen mit typeid(a).name() . Und dieses neue Tool baut auf folgenden Elementen auf typeid(a).name() .

Die grundlegende Frage:

typeid(a).name()

wirft cv-Qualifier, Referenzen und lvalue/rvalue-ness weg. Zum Beispiel:

const int ci = 0;
std::cout << typeid(ci).name() << '\n';

Für mich Ausgaben:

i

und ich gehe davon aus, dass es sich um MSVC-Ausgaben handelt:

int

D.h. die const ist weg. Dies ist kein Problem der QOI (Quality of Implementation). Der Standard schreibt dieses Verhalten vor.

Was ich im Folgenden empfehle, ist:

template <typename T> std::string type_name();

die wie folgt verwendet werden würde:

const int ci = 0;
std::cout << type_name<decltype(ci)>() << '\n';

und für mich Ausgaben:

int const

<disclaimer> Ich habe dies nicht unter MSVC getestet. </disclaimer> Aber ich freue mich über das Feedback derjenigen, die das tun.

Die C++11-Lösung

Ich verwende __cxa_demangle für Nicht-MSVC-Plattformen gemäß der Empfehlung von ipapadop in seiner Antwort auf demangle types. Aber unter MSVC vertraue ich auf typeid Namen zu entwirren (nicht getestet). Und dieser Kern ist um einige einfache Tests gewickelt, die cv-Qualifier und Verweise auf den Eingabetyp erkennen, wiederherstellen und melden.

#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;
}

Die Ergebnisse

Mit dieser Lösung kann ich das tun:

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';
}

und die Ausgabe ist:

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

Beachten Sie (zum Beispiel) den Unterschied zwischen decltype(i) y decltype((i)) . Ersteres ist der Typ des Erklärung de i . Letzteres ist der "Typ" des Ausdruck i . (Ausdrücke haben nie einen Referenztyp, aber als Konvention decltype steht für l-wertige Ausdrücke mit l-wertigen Referenzen).

Daher ist dieses Tool ein hervorragendes Instrument, um sich über decltype zusätzlich zum Erforschen und Debuggen Ihres eigenen Codes.

Wenn ich dies hingegen nur auf typeid(a).name() ohne das Hinzufügen von verlorenen cv-Qualifizierern oder Referenzen, wäre die Ausgabe:

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

D.h. jede Referenz und jeder Lebenslaufqualifizierer wird entfernt.

C++14 Update

Gerade wenn man denkt, dass man eine Lösung für ein Problem gefunden hat, kommt immer jemand aus dem Nichts und zeigt einem einen viel besseren Weg :-)

Diese Antwort de Jamboree zeigt, wie man den Typnamen in C++14 zur Kompilierzeit erhält. Dies ist aus mehreren Gründen eine brillante Lösung:

  1. Es ist die Kompilierzeit!
  2. Sie lassen den Compiler selbst die Arbeit erledigen und nicht eine Bibliothek (sogar eine std::lib). Dies bedeutet genauere Ergebnisse für die neuesten Sprachfunktionen (wie Lambdas).

Jamboree's 回答 nicht ganz alles für VS, und ich bin Tweaking seinen Code ein wenig. Aber da diese Antwort eine Menge Ansichten bekommt, nehmen Sie sich etwas Zeit, um dorthin zu gehen und upvote seine Antwort, ohne die, dieses Update würde nie geschehen sein.

#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
}

Dieser Code sorgt für eine automatische Rückstellung bei der constexpr wenn Sie noch im alten C++11 feststecken. Und wenn Sie mit C++98/03 an der Höhlenwand malen, ist die noexcept wird ebenfalls geopfert.

C++17 Update

In den Kommentaren unten Lyberta weist darauf hin, dass die neue std::string_view ersetzen können 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
}

Ich habe die Konstanten für VS dank der sehr guten Detektivarbeit von Jive Dadson in den Kommentaren unten aktualisiert.

Aktualisierung:

Schauen Sie unbedingt nach diese Neuformulierung unten wodurch die unleserlichen magischen Zahlen in meiner letzten Formulierung wegfallen.

4 Stimmen

VS 14 CTP druckt korrekte Typen aus, ich musste nur einen hinzufügen #include <iostream> Linie.

3 Stimmen

Warum template<typename T>std::string type_name()? Warum übergeben Sie nicht einen Typ als Argument?

2 Stimmen

Ich glaube, ich habe das damit begründet, dass ich manchmal nur einen Typ hatte (z.B. einen abgeleiteten Template-Parameter), und ich wollte nicht künstlich einen davon konstruieren müssen, um den Typ zu erhalten (obwohl heutzutage declval würde die Aufgabe erfüllen).

271voto

Konrad Rudolph Punkte 503837

Versuchen Sie es:

#include <typeinfo>

// …
std::cout << typeid(a).name() << '\n';

Möglicherweise müssen Sie RTTI in Ihren Compileroptionen aktivieren, damit dies funktioniert. Außerdem hängt die Ausgabe von dem Compiler ab. Es kann ein roher Typname oder ein Symbol zur Namensumwandlung oder etwas dazwischen sein.

8 Stimmen

Warum ist die von der Funktion name() zurückgegebene Zeichenfolge durch die Implementierung definiert?

8 Stimmen

@PravasiMeet Soweit ich weiß, gibt es keinen guten Grund. Der Ausschuss wollte die Compiler-Implementierer einfach nicht in bestimmte technische Richtungen zwingen - im Nachhinein betrachtet wahrscheinlich ein Fehler.

2 Stimmen

Gibt es ein Flag, mit dem ich RTTI aktivieren kann? Vielleicht können Sie Ihre Antwort umfassender gestalten.

162voto

康桓瑋 Punkte 18124

Selon Howard Wenn Sie die magische Zahl nicht mögen, ist dies eine gute Möglichkeit der Darstellung und sieht intuitiv aus:

#include <string_view>

template <typename T>
constexpr auto type_name() {
  std::string_view name, prefix, suffix;
#ifdef __clang__
  name = __PRETTY_FUNCTION__;
  prefix = "auto type_name() [T = ";
  suffix = "]";
#elif defined(__GNUC__)
  name = __PRETTY_FUNCTION__;
  prefix = "constexpr auto type_name() [with T = ";
  suffix = "]";
#elif defined(_MSC_VER)
  name = __FUNCSIG__;
  prefix = "auto __cdecl type_name<";
  suffix = ">(void)";
#endif
  name.remove_prefix(prefix.size());
  name.remove_suffix(suffix.size());
  return name;
}

Demo.

11 Stimmen

Dies ist eine großartige Zusammenfassung der Bemühungen der letzten C++-Versionen in etwas, das kurz und bündig ist. +1.

8 Stimmen

Das ist auch mein Favorit!

2 Stimmen

Hier eine ähnliche Funktion, die ich verwende und die das Suffix/Präfix automatisch erkennt: stackoverflow.com/questions/1055452/

133voto

NickV Punkte 1436

Sehr hässlich, aber gut geeignet, wenn Sie nur Informationen zur Kompilierzeit benötigen (z. B. für die Fehlersuche):

auto testVar = std::make_tuple(1, 1.0, "abc");
decltype(testVar)::foo= 1;

Rückgabe:

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*>'

18 Stimmen

Nur C++ könnte dies so schwierig machen (Drucken einer Auto-Variablen Typ zur Kompilierzeit). NUR C++.

3 Stimmen

@KarlP nun, um fair zu sein, es ist ein wenig verworren, das funktioniert auch :) auto testVar = std::make_tuple(1, 1.0, "abc"); decltype(testVar)::foo = 1;

0 Stimmen

Unter VC++17 reduziert dies eine rvalue-Referenz auf eine einfache Referenz, selbst in einer Template-Funktion mit Forwarding-Referenz-Parameter und dem in std::forward verpackten Objektnamen.

63voto

mdec Punkte 4844

Vergessen Sie nicht <typeinfo>

Ich glaube, was Sie meinen, ist die Identifizierung von Laufzeittypen. Sie können das oben genannte erreichen, indem Sie .

#include <iostream>
#include <typeinfo>

using namespace std;

int main() {
  int i;
  cout << typeid(i).name();
  return 0;
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X