Zum Beispiel:
int a = 12;
cout << typeof(a) << endl;
Erwartetes Ergebnis:
int
Zum Beispiel:
int a = 12;
cout << typeof(a) << endl;
Erwartetes Ergebnis:
int
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:
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.
Schauen Sie unbedingt nach diese Neuformulierung unten wodurch die unleserlichen magischen Zahlen in meiner letzten Formulierung wegfallen.
VS 14 CTP druckt korrekte Typen aus, ich musste nur einen hinzufügen #include <iostream>
Linie.
Warum template<typename T>std::string type_name()? Warum übergeben Sie nicht einen Typ als Argument?
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).
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.
Warum ist die von der Funktion name() zurückgegebene Zeichenfolge durch die Implementierung definiert?
@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.
Gibt es ein Flag, mit dem ich RTTI aktivieren kann? Vielleicht können Sie Ihre Antwort umfassender gestalten.
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;
}
Dies ist eine großartige Zusammenfassung der Bemühungen der letzten C++-Versionen in etwas, das kurz und bündig ist. +1.
Hier eine ähnliche Funktion, die ich verwende und die das Suffix/Präfix automatisch erkennt: stackoverflow.com/questions/1055452/
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*>'
Nur C++ könnte dies so schwierig machen (Drucken einer Auto-Variablen Typ zur Kompilierzeit). NUR C++.
@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;
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.
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.
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 gedrucktvoid print_T() [T = const int *const **]
zur Laufzeit und bewahrt alle Qualifier (funktioniert in GCC und Clang).0 Stimmen
@Henri,
__PRETTY_FUNCTION__
ist nicht Standard C++ (die Anforderung steht im Titel der Frage).