例如: 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