是否能夠在標準C ++中打印變量的類型?

例如: ios

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

預期產量: 函數

int

#1樓

很是醜陋,可是若是您只想要編譯時間信息(例如,用於調試),則能夠達到目的: 工具

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

#2樓

如前所述, 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


#3樓

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中的類型名稱。 這是一個出色的解決方案,其緣由以下:

  1. 在編譯時!
  2. 您可讓編譯器自己代替庫(甚至是std :: lib)來執行此工做。 這意味着使用最新的語言功能(例如lambda)可得到更準確的結果。

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的常數。

更新:

請確保在下面檢查此重寫,以消除我最新公式中沒法理解的幻數。


#4樓

我喜歡尼克的方法,一個完整的表格多是這樣的(對於全部基本數據類型):

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*"; }

#5樓

一個比我之前的解決方案更通用的解決方案,沒有函數重載:

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
相關文章
相關標籤/搜索