Par exemple:
int a = 12;
cout << typeof(a) << endl;
Résultat attendu:
int
Par exemple:
int a = 12;
cout << typeof(a) << endl;
Résultat attendu:
int
Mise à jour de C++11 à une très vieille question : Imprimer le type de variable en C++.
La réponse acceptée (et bonne) est d'utiliser typeid(a).name()
, donde a
est un nom de variable.
En C++11, nous avons decltype(x)
qui peut transformer une expression en un type. Et decltype()
s'accompagne de son propre ensemble de règles très intéressantes. Par exemple decltype(a)
y decltype((a))
seront généralement de types différents (et pour des raisons bonnes et compréhensibles une fois que ces raisons sont exposées).
Est-ce que notre fidèle typeid(a).name()
nous aider à explorer ce nouveau monde ?
Non.
Mais l'outil qui le fera n'est pas si compliqué. Et c'est cet outil que j'utilise pour répondre à cette question. Je vais comparer et contraster ce nouvel outil avec typeid(a).name()
. Et ce nouvel outil est en fait construit au dessus de typeid(a).name()
.
La question fondamentale :
typeid(a).name()
élimine les qualificatifs cv, les références, et les valeurs lvalue/rvalue-ness. Par exemple :
const int ci = 0;
std::cout << typeid(ci).name() << '\n';
Pour moi, des sorties :
i
et je devine sur les sorties MSVC :
int
C'est-à-dire le const
est parti. Il ne s'agit pas d'un problème de QOI (Quality Of Implementation). La norme impose ce comportement.
Ce que je recommande ci-dessous est :
template <typename T> std::string type_name();
qui serait utilisé comme suit :
const int ci = 0;
std::cout << type_name<decltype(ci)>() << '\n';
et pour moi des sorties :
int const
<disclaimer>
Je n'ai pas testé cela sur MSVC. </disclaimer>
Mais j'accueille volontiers les commentaires de ceux qui le font.
La solution C++11
J'utilise __cxa_demangle
pour les plates-formes non-MSVC, comme le recommande la Commission européenne. ipapadop dans sa réponse aux types de démêlés. Mais sur MSVC, je fais confiance à typeid
pour démêler les noms (non testé). Et ce noyau est enveloppé autour de quelques tests simples qui détectent, restaurent et rapportent les cv-qualificateurs et les références au type d'entrée.
#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;
}
Les résultats
Avec cette solution, je peux le faire :
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';
}
et la sortie est :
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
Notez (par exemple) la différence entre decltype(i)
y decltype((i))
. Le premier est le type de déclaration de i
. Ce dernier est le "type" de la expression i
. (les expressions n'ont jamais de type de référence, mais par convention decltype
représente des expressions de type lvalue avec des références de type lvalue).
Ainsi, cet outil est un excellent moyen d'apprendre à connaître decltype
en plus de l'exploration et du débogage de votre propre code.
En revanche, si je devais construire ceci juste sur typeid(a).name()
sans ajouter les qualificatifs ou références cv perdus, le résultat serait le suivant :
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
C'est-à-dire que chaque référence et chaque qualificatif de cv est supprimé.
Mise à jour du C++14
Juste au moment où vous pensez avoir trouvé la solution à un problème, quelqu'un sort toujours de nulle part et vous montre une bien meilleure solution :-)
Cette réponse de Jamboree montre comment obtenir le nom du type en C++14 au moment de la compilation. C'est une solution brillante pour plusieurs raisons :
Jamboree réponse ne met pas tout à plat pour VS, et je modifie un peu son code. Mais puisque cette réponse reçoit beaucoup de vues, prenez le temps d'y aller et d'upvoter sa réponse, sans quoi, cette mise à jour n'aurait jamais eu lieu.
#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
}
Ce code déclenchera le retour automatique sur le constexpr
si vous êtes toujours coincé dans l'ancien C++11. Et si vous peignez sur le mur de la caverne avec C++98/03, les noexcept
est également sacrifié.
Mise à jour C++17
Dans les commentaires ci-dessous Lyberta souligne que la nouvelle std::string_view
peut remplacer 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
}
J'ai mis à jour les constantes pour VS grâce au très bon travail de détective de Jive Dadson dans les commentaires ci-dessous.
N'oubliez pas de consulter cette réécriture ci-dessous ce qui élimine les chiffres magiques illisibles dans ma dernière formulation.
Je pense que ma justification était que parfois je n'avais que un type (comme un paramètre de modèle déduit), et je ne voulais pas devoir en construire artificiellement un pour obtenir le type (bien que de nos jours declval
ferait l'affaire).
Essayer :
#include
// …
std::cout << typeid(a).name() << '\n';
Vous devrez peut-être activer RTTI dans les options de votre compilateur pour que cela fonctionne. De plus, la sortie de ceci dépend du compilateur. Il peut s'agir d'un nom de type brut ou d'un symbole de mangle de nom ou de tout ce qui se situe entre les deux.
@PravasiMeet Aucune bonne raison, à ma connaissance. Le comité ne voulait tout simplement pas contraindre les implémenteurs de compilateurs dans des directions techniques particulières - probablement une erreur, rétrospectivement.
Y a-t-il un drapeau que je pourrais utiliser pour activer le RTTI? Peut-être pourriez-vous rendre votre réponse inclusive.
Très laid mais fait l'affaire si vous ne voulez que des informations au moment de la compilation (par exemple pour le débogage) :
auto testVar = std::make_tuple(1, 1.0, "abc");
decltype(testVar)::foo= 1;
Renvoie :
La compilation s'est terminée avec des erreurs :
source.cpp: Dans la fonction 'int main()':
source.cpp:5:19: erreur : 'foo' n'est pas un membre de 'std::tuple'
Seul c++ pourrait rendre cela si difficile (imprimer le type d'une variable automatique au moment de la compilation). SEULEMENT C++.
@KarlP eh bien pour être juste c'est un peu compliqué, cela fonctionne aussi :) auto testVar = std::make_tuple(1, 1.0, "abc"); decltype(testVar)::foo = 1;
Sur VC++17, cela réduit une référence rvalue à une référence simple, même dans une fonction modèle avec un paramètre de forwarding-reference, et le nom de l'objet est enveloppé dans std::forward.
Notez que les noms générés par la fonctionnalité RTTI de C++ ne sont pas portables. Par exemple, la classe
MyNamespace::CMyContainer
aura les noms suivants:
// MSVC 2003:
class MyNamespace::CMyContainer[int,class test_MyNamespace::CMyObject]
// G++ 4.2:
N8MyNamespace8CMyContainerIiN13test_MyNamespace9CMyObjectEEE
Vous ne pouvez donc pas utiliser cette information pour la sérialisation. Mais néanmoins, la propriété typeid(a).name() peut toujours être utilisée à des fins de journalisation/débogage.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
2 votes
Voici un résumé de la solution longue de Howard, mais implémentée avec un macro hérétique d'une seule ligne :
#define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL)
. Si vous avez besoin de support multiplateforme : Utilisez#ifdef
,#else
,#endif
pour fournir des macros pour d'autres plateformes comme MSVC.0 votes
Avec une exigence plus explicite lisible par l'homme : stackoverflow.com/questions/12877521/…
3 votes
Si vous utilisez cela uniquement pour le débogage, vous voudrez peut-être envisager
template void print_T() { std::cout << __PRETTY_FUNCTION__ << '\n'; }
. Ensuite, en utilisant par exempleprint_T();
afficheravoid print_T() [T = const int *const **]
à l'exécution et conservera tous les qualificateurs (fonctionne dans GCC et Clang).0 votes
@Henri,
__PRETTY_FUNCTION__
n'est pas du C++ standard (l'exigence est dans le titre de la question).