例如: ios
int a = 12; cout << typeof(a) << endl;
预期产量: 函数
int
很是丑陋,可是若是您只想要编译时间信息(例如,用于调试),则能够达到目的: 工具
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*>'
如前所述, 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;
} spa
C ++ 11更新为一个很是老的问题:在C ++中打印变量类型。 调试
公认的(也是好的)答案是使用typeid(a).name()
,其中a
是变量名。 code
如今在C ++ 11中,咱们有了decltype(x)
,它能够将表达式转换为类型。 并且decltype()
带有其本身的一组很是有趣的规则。 例如, decltype(a)
和decltype((a))
一般将是不一样的类型(而且出于良好且易于理解的缘由,一旦暴露了这些缘由)。 ip
咱们可信赖的typeid(a).name()
帮助咱们探索这个勇敢的新世界吗? ci
没有。
可是将不会那么复杂的工具。 这就是我用来回答这个问题的工具。 我将把这个新工具与typeid(a).name()
进行比较和对比。 这个新工具其实是在typeid(a).name()
之上构建的。
基本问题:
typeid(a).name()
丢弃cv限定词,引用和左值/右值-ness。 例如:
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解决方案
根据ipapadop在他对demangle类型的回答中的建议,我正在将__cxa_demangle
用于非MSVC平台。 可是在MSVC上,我相信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
即剥夺了每一个参考和简历限定词。
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 }
若是您仍然停留在古老的C ++ 11中,则此代码将在constexpr
上自动回退。 并且,若是您要使用C ++ noexcept
在洞穴墙壁上绘画,那么noexcept
也将被牺牲。
C ++ 17更新
在下面的评论中, Lyberta指出新的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的常数。
我喜欢尼克的方法,一个完整的表格多是这样的(对于全部基本数据类型):
template <typename T> const char* typeof(T&) { return "unknown"; } // default template<> const char* typeof(int&) { return "int"; } template<> const char* typeof(short&) { return "short"; } template<> const char* typeof(long&) { return "long"; } template<> const char* typeof(unsigned&) { return "unsigned"; } template<> const char* typeof(unsigned short&) { return "unsigned short"; } template<> const char* typeof(unsigned long&) { return "unsigned long"; } template<> const char* typeof(float&) { return "float"; } template<> const char* typeof(double&) { return "double"; } template<> const char* typeof(long double&) { return "long double"; } template<> const char* typeof(std::string&) { return "String"; } template<> const char* typeof(char&) { return "char"; } template<> const char* typeof(signed char&) { return "signed char"; } template<> const char* typeof(unsigned char&) { return "unsigned char"; } template<> const char* typeof(char*&) { return "char*"; } template<> const char* typeof(signed char*&) { return "signed char*"; } template<> const char* typeof(unsigned char*&) { return "unsigned char*"; }
一个比我之前的解决方案更通用的解决方案,没有函数重载:
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