Bir değişkenin türünü standart C ++ ile yazdırmak mümkün mü?


393

Örneğin:

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

Beklenen çıktı:

int

2
İşte Howard'ın uzun biçim çözümün bir özetidir ama sapkın tek satırlı makroyla uygulanması: #define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL). Eğer çapraz platform desteği gerekirse: kullan #ifdef, #else, #endifMSVC gibi diğer platformlar için bir makro sağlamaktır.
Trevor Boyd Smith


3
Bunu yalnızca hata ayıklama için kullanıyorsanız, dikkate almak isteyebilirsiniz template<typename T> void print_T() { std::cout << __PRETTY_FUNCTION__ << '\n'; }. Daha sonra örneğin kullanmak çalışma zamanında print_T<const int * const **>();yazdırır void print_T() [T = const int *const **]ve tüm niteleyicileri korur (GCC ve Clang'da çalışır).
Henri Menke

@Henri, __PRETTY_FUNCTION__Standart C ++ değildir (gereksinim soru başlığındadır).
Toby Speight

Yanıtlar:


505

C ++ 11 çok eski bir soruya güncelleme: C ++ 'da değişken türü yazdırın.

Kabul (ve iyi) cevap kullanımına olan typeid(a).name(), adeğişken adıdır.

Şimdi C ++ 11'de decltype(x)bir ifadeyi bir türe dönüştürebiliriz. Ve decltype()kendi çok ilginç kuralları ile geliyor. Örneğin decltype(a)ve decltype((a))genellikle farklı türler olacaktır (ve bu nedenler ortaya çıktıktan sonra iyi ve anlaşılabilir nedenlerle).

Güvenilirimiz typeid(a).name()bu cesur yeni dünyayı keşfetmemize yardımcı olacak mı?

Hayır.

Ama olacak araç o kadar da karmaşık değil. Ve bu soruya cevap olarak kullandığım araç. Bu yeni aracı karşılaştırıp kıyaslayacağım typeid(a).name(). Ve bu yeni araç aslında üstüne inşa edilmiştir typeid(a).name().

Temel konu:

typeid(a).name()

cv niteleyicileri, referansları ve lvalue / rvalue-ness'i atar. Örneğin:

const int ci = 0;
std::cout << typeid(ci).name() << '\n';

Benim için çıktılar:

i

ve MSVC çıktılarını tahmin ediyorum:

int

Yani constgitti. Bu bir QOI (Uygulama Kalitesi) sorunu değildir. Standart bu davranışı zorunlu kılar.

Aşağıda önerdiğim şey:

template <typename T> std::string type_name();

bu şekilde kullanılır:

const int ci = 0;
std::cout << type_name<decltype(ci)>() << '\n';

ve benim için çıktılar:

int const

<disclaimer>Bunu MSVC'de test etmedim. </disclaimer> Ama bunu yapanların geri bildirimlerini memnuniyetle karşılıyoruz.

C ++ 11 Çözümü

Ben kullanıyorum __cxa_demangletarafından tavsiye olmayan MSVC platformları için ipapadop demangle türlerine onun cevabını. Ama typeidMSVC'de isimleri (denenmemiş) demangle etmeye güveniyorum . Ve bu çekirdek, cv niteleyicileri ve giriş türüne referansları algılayan, geri yükleyen ve raporlayan bazı basit testlerin etrafına sarılır.

#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;
}

Sonuçlar

Bu çözüm ile bunu yapabilirim:

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';
}

ve çıktı:

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

Not (örneğin) arasındaki fark, decltype(i)ve decltype((i)). Eski türüdür beyanı arasında i. İkincisi, ifadenin "tipidir" i. (ifadeler hiçbir zaman referans tipine sahip değildir, ancak bir kural olarak, değer referansları olan değer decltypebiçimli ifadeleri temsil eder).

Böylece bu araç, decltypekendi kodunuzu keşfetmeye ve hata ayıklamaya ek olarak, sadece öğrenmek için mükemmel bir araçtır .

Bunun tersine, bunu typeid(a).name()kayıp cv niteleyicileri veya referansları eklemeden, sadece üzerine inşa edersem , çıktı:

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

Yani Her referans ve cv niteleyicisi çıkarılır.

C ++ 14 Güncellemesi

Sadece çivilenmiş bir soruna bir çözümünüz olduğunu düşündüğünüzde, birisi her zaman yoktan gelir ve size çok daha iyi bir yol gösterir. :-)

Jamboree'den gelen bu cevap , derleme zamanında C ++ 14'te tür adının nasıl alınacağını gösterir. Birkaç nedenden dolayı mükemmel bir çözümdür:

  1. Derleme zamanı!
  2. Derleyicinin kendisi bir kütüphane yerine işi yapmasını sağlar (hatta bir std :: lib). Bu, en son dil özellikleri için (lambdas gibi) daha doğru sonuçlar anlamına gelir.

Jamboree'un cevabı VS için her şeyi ortaya koymuyor ve kodunu biraz değiştiriyorum. Ancak bu cevap çok fazla görüş aldığından, oraya gitmek için biraz zaman ayırın ve cevabını oylayın, bu olmadan bu güncelleme asla olmazdı.

#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
}

constexprHala eski C ++ 11'de takılı kalırsanız bu kod otomatik olarak geri çekilir . Ve eğer C ++ 98/03 ile mağara duvarında resim yapıyorsanız, bu noexceptda feda edilir.

C ++ 17 Güncellemesi

Aşağıdaki yorumlarda Lyberta , yeninin std::string_viewyerini alabileceğine dikkat çekiyor 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
}

Aşağıdaki yorumlarda Jive Dadson'un çok güzel dedektiflik çalışmaları sayesinde VS sabitlerini güncelledim.

Güncelleme:

En son formülasyonumdaki okunamayan sihirli sayıları ortadan kaldıran aşağıdaki bu yeniden yazmayı kontrol ettiğinizden emin olun .


4
VS 14 CTP doğru tipleri yazdırdı, sadece bir #include <iostream>satır eklemek zorunda kaldım .
Max Galkin

3
Neden <typename T> std :: string type_name () şablonu? Neden bir türü argüman olarak geçirmiyorsunuz?
moonman239

2
Benim mantığımın bazen sadece bir türümün (çıkarılan bir şablon parametresi gibi) olduğumu ve türünü almak için yapay olarak bunlardan birini inşa etmek istemediğime inanıyorum (bu günlerde declvaliş yapacaktı).
Howard Hinnant

5
@AngelusMortis: İngilizce, C ++ koduna kıyasla belirsiz / belirsiz olduğundan, bunu ilgilendiğiniz belirli türle ve ilgilendiğiniz belirli derleyiciyle test durumunuza kopyalamanızı / yapıştırmanızı ve daha fazlasını yazmanızı öneririm sonuç şaşırtıcı ve / veya tatmin edici değilse ayrıntılar.
Howard Hinnant

3
@HowardHinnant std::string_viewyerine kullanabilirsiniz static_string?
Lyberta

231

Deneyin:

#include <typeinfo>

// …
std::cout << typeid(a).name() << '\n';

Bunun çalışması için derleyici seçeneklerinizde RTTI'yi etkinleştirmeniz gerekebilir. Ayrıca, bunun çıktısı derleyiciye bağlıdır. Bir ham tür adı veya ad düzenleme sembolü veya aradaki herhangi bir şey olabilir.


4
Name () işleviyle döndürülen dize neden uygulama tanımlı?
Destructor

4
@PravasiMeet Bildiğim kadarıyla iyi bir neden yok. Komite, derleyici uygulayıcılarını belirli teknik yönlere zorlamak istemiyordu - muhtemelen bir hata, arkada.
Konrad Rudolph

2
RTTI'yi etkinleştirmek için kullanabileceğim bir bayrak var mı? Belki cevabınızı kapsayıcı yapabilirsiniz.
Jim

4
@Destructor Standart bir ad yönetim biçimi sağlamak, iki farklı derleyici tarafından oluşturulan ikili dosyalar arasında birlikte çalışabilirliğin mümkün olmadığı ve / veya güvenli olduğu izlenimini verebilir. C ++ standart bir ABI'ye sahip olmadığından, standart bir ad düzenleme şeması anlamsız ve potansiyel olarak yanıltıcı ve tehlikeli olabilir.
Elkvis

1
@Jim Derleyici bayrakları bölümü, yanıtın kendisinden daha uzun bir büyüklük sırası olacaktır. GCC varsayılan olarak onunla derler, bu nedenle "-fno-rtti", diğer derleyiciler bunu yapmamayı seçebilir, ancak derleyici bayrakları için standart yoktur.
kfsone

82

Çok çirkin ama sadece zaman bilgisi derlemek istiyorsanız hile yapar (örneğin, hata ayıklama için):

auto testVar = std::make_tuple(1, 1.0, "abc");
decltype(testVar)::foo= 1;

İadeler:

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
yalnızca c ++ bunu zorlaştırabilir (derleme zamanında bir otomatik değişken türü yazdırma). SADECE C ++.
Karl Pickett

3
@KarlP iyi olmak biraz kıvrımlı, bu da işe yarıyor :) auto testVar = std::make_tuple(1, 1.0, "abc"); decltype(testVar)::foo = 1;
NickV

VC ++ 17'de, yönlendirme başvurusu parametresine sahip bir şablon işlevinde ve std :: forward içine sarılmış nesne adında bile düz referansa bir rvalue referansını azaltır.
Jive Dadson

Yeni tekerlekler oluşturmadan bu tipe ulaşabildiniz!
Steven Eckhoff

1
Bu teknik, Etkili Modern C ++ 'da "Öğe 4:
Çıkartılan

54

Dahil etmeyi unutma <typeinfo>

Ne demek istediğiniz çalışma zamanı türü tanımlama olduğuna inanıyorum. Yukarıdakileri yaparak başarabilirsiniz.

#include <iostream>
#include <typeinfo>

using namespace std;

int main() {
  int i;
  cout << typeid(i).name();
  return 0;
}

36

Göre Howard sihirli sayı istemiyorsanız 'ın çözümü, ben bu temsil etmek için iyi bir yoldur ve sezgisel görünüyor düşünüyorum:

#include <string_view>

template <typename T>
constexpr std::string_view 
type_name()
{
    std::string_view name, prefix, suffix;
#ifdef __clang__
    name = __PRETTY_FUNCTION__;
    prefix = "std::string_view type_name() [T = ";
    suffix = "]";
#elif defined(__GNUC__)
    name = __PRETTY_FUNCTION__;
    prefix = "constexpr std::string_view type_name() [with T = ";
    suffix = "; std::string_view = std::basic_string_view<char>]";
#elif defined(_MSC_VER)
    name = __FUNCSIG__;
    prefix = "class std::basic_string_view<char,struct std::char_traits<char> > __cdecl type_name<";
    suffix = ">(void)";
#endif
    name.remove_prefix(prefix.size());
    name.remove_suffix(suffix.size());
    return name;
}

4
Bu, geçtiğimiz birkaç C ++ sürümündeki çabaların kısa ve tatlı bir şeye büyük bir damıtmasıdır. +1.
einpoklum

1
Bu da benim favorim!
Howard Hinnant

1
Burada, eki / öneki otomatik olarak algılayan, kullandığım benzer bir işlev: stackoverflow.com/questions/1055452/…
HolyBlackCat

22

C ++ RTTI özelliği tarafından oluşturulan adların taşınabilir olmadığını unutmayın . Örneğin, sınıf

MyNamespace::CMyContainer<int, test_MyNamespace::CMyObject>

aşağıdaki isimlere sahip olacak:

// MSVC 2003:
class MyNamespace::CMyContainer[int,class test_MyNamespace::CMyObject]
// G++ 4.2:
N8MyNamespace8CMyContainerIiN13test_MyNamespace9CMyObjectEEE

Dolayısıyla bu bilgileri serileştirme için kullanamazsınız. Ancak yine de, typeid (a) .name () özelliği günlük / hata ayıklama amacıyla kullanılabilir


19

Şablonları kullanabilirsiniz.

template <typename T> const char* typeof(T&) { return "unknown"; }    // default
template<> const char* typeof(int&) { return "int"; }
template<> const char* typeof(float&) { return "float"; }

Yukarıdaki örnekte, tür eşleşmediğinde "bilinmiyor" yazdıracaktır.


3
Şort ve karakter için "int" yazmaz mı? Ve çiftler için "yüzer"?
gartenriese

1
@gartenriese Specialization'ın bu dezavantajı yoktur. Çünkü doubleuzmanlığı kullanmak için örtük bir tür dönüştürme yapmak yerine şablon işlevinin özel olmayan sürümünü derlerdi
chappjc

1
@chappjc: Dürüst olmak gerekirse bunu neden sorduğumu bilmiyorum, şimdi benim için oldukça açık. Ama yine de bir yaşındaki soruyu cevapladığınız için teşekkürler!
Mart'ta

2
@gartenriese Ben çok düşündüm, ama "internet" bir noktada aynı soru olabilir.
chappjc

18

Belirtildiği gibi, typeid().name()karışık bir ad döndürebilir. GCC'de (ve diğer bazı derleyicilerde) aşağıdaki kodla çalışabilirsiniz:

#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;

}


10

Bunun için bir özellik sınıfı kullanabilirsiniz. Gibi bir şey:

#include <iostream>
using namespace std;

template <typename T> class type_name {
public:
    static const char *name;
};

#define DECLARE_TYPE_NAME(x) template<> const char *type_name<x>::name = #x;
#define GET_TYPE_NAME(x) (type_name<typeof(x)>::name)

DECLARE_TYPE_NAME(int);

int main()
{
    int a = 12;
    cout << GET_TYPE_NAME(a) << endl;
}

Bu DECLARE_TYPE_NAMEtanım, ihtiyaç duyduğunuz tüm türler için bu özellik sınıfını bildirirken hayatınızı kolaylaştırmak için vardır.

typeidÇıktıyı kontrol edebildiğiniz için bu , ilgili çözümlerden daha yararlı olabilir . Örneğin, kullanma typeidiçin long longbenim derleyici üzerinde "x" verir.


10

C ++ 11'de decltype var. Standart c ++ 'da decltype kullanılarak bildirilen değişkenin tam tipini görüntülemenin bir yolu yoktur. type_id_with_cvrAşağıdaki gibi yazdırmak için boost typeindex yani (cvr const, volatile, referans anlamına gelir) kullanabiliriz.

#include <iostream>
#include <boost/type_index.hpp>

using namespace std;
using boost::typeindex::type_id_with_cvr;

int main() {
  int i = 0;
  const int ci = 0;
  cout << "decltype(i) is " << type_id_with_cvr<decltype(i)>().pretty_name() << '\n';
  cout << "decltype((i)) is " << type_id_with_cvr<decltype((i))>().pretty_name() << '\n';
  cout << "decltype(ci) is " << type_id_with_cvr<decltype(ci)>().pretty_name() << '\n';
  cout << "decltype((ci)) is " << type_id_with_cvr<decltype((ci))>().pretty_name() << '\n';
  cout << "decltype(std::move(i)) is " << type_id_with_cvr<decltype(std::move(i))>().pretty_name() << '\n';
  cout << "decltype(std::static_cast<int&&>(i)) is " << type_id_with_cvr<decltype(static_cast<int&&>(i))>().pretty_name() << '\n';
  return 0;
}

1
bir yardımcı işlevi kullanmak daha kolay olurdu:template<typename T> void print_type(T){cout << "type T is: "<< type_id_with_cvr<T>().pretty_name()<< '\n';}
r0ng 18:17

6

Ayrıca, tür adını demangle etmek için -t (tür) seçeneğiyle birlikte c ++ filtresini de kullanabilirsiniz:

#include <iostream>
#include <typeinfo>
#include <string>

using namespace std;

int main() {
  auto x = 1;
  string my_type = typeid(x).name();
  system(("echo " + my_type + " | c++filt -t").c_str());
  return 0;
}

Yalnızca linux üzerinde test edilmiştir.


1
Cehennem çirkin ama ihtiyacım olanı yapacak. Ve diğer çözümlerden çok daha küçük. Mac btw üzerinde çalışır.
Marco Luglio

6

Howard Hinnant, tür adını ayıklamak için sihirli sayılar kullandı 瑋 桓 瑋 önerilen dize öneki ve soneki. Ancak önek / sonek değişmeye devam eder. “Probe_type” ile type_name, tür adını ayıklamak için “probe_type” için önek ve sonek boyutlarını otomatik olarak hesaplar:

#include <iostream>
#include <string_view>

using namespace std;

class probe_type;

template <typename T>
constexpr string_view type_name() {
  string_view probe_type_name("class probe_type");
  const string_view class_specifier("class");

  string_view name;
#ifdef __clang__
  name = __PRETTY_FUNCTION__;
  probe_type_name.remove_prefix(class_specifier.length());
#elif defined(__GNUC__)
  name = __PRETTY_FUNCTION__;
  probe_type_name.remove_prefix(class_specifier.length());
#elif defined(_MSC_VER)
  name = __FUNCSIG__;
#endif

  if (name.find(probe_type_name) != string_view::npos)
    return name;

  const string_view probe_type_raw_name = type_name<probe_type>();

  const size_t prefix_size = probe_type_raw_name.find(probe_type_name);

  name.remove_prefix(prefix_size);
  name.remove_suffix(probe_type_raw_name.length() - prefix_size - probe_type_name.length());

  return name;
}

class test;

int main() {
  cout << type_name<test>() << endl;

  cout << type_name<const int*&>() << endl;
  cout << type_name<unsigned int>() << endl;

  const int ic = 42;
  const int* pic = &ic;
  const int*& rpic = pic;
  cout << type_name<decltype(ic)>() << endl;
  cout << type_name<decltype(pic)>() << endl;
  cout << type_name<decltype(rpic)>() << endl;

  cout << type_name<probe_type>() << endl;
}

Çıktı

gcc 10.0.0 20190919 Değnek kutusu:

 test
 const int *&
 unsigned int
 const int
 const int *
 const int *&
 constexpr std::string_view type_name() [with T = probe_type; std::string_view = std::basic_string_view<char>]

clang 10.0.0 Wandbox:

 test
 const int *&
 unsigned int
 const int
 const int *
 const int *&
 std::__1::string_view type_name() [T = probe_type]

VS 2019 sürüm 16.3.3:

class test
const int*&
unsigned int
const int
const int*
const int*&
class std::basic_string_view<char,struct std::char_traits<char> > __cdecl type_name<class probe_type>(void)

5

RTTI (tip kimliği) ile ilgili diğer cevaplar, şu sürece muhtemelen istediğiniz şeydir:

  • bellek yükünü karşılayabilirsiniz (bazı derleyicilerde önemli olabilir)
  • derleyicinizin döndürdüğü sınıf adları yararlıdır

Alternatif, (Greg Hewgill'in cevabına benzer şekilde), derleme zamanı özellikleri tablosu oluşturmaktır.

template <typename T> struct type_as_string;

// declare your Wibble type (probably with definition of Wibble)
template <>
struct type_as_string<Wibble>
{
    static const char* const value = "Wibble";
};

Bildirimleri bir makroya sarırsanız, virgül nedeniyle birden fazla parametre (örneğin std :: map) alan şablon türleri için ad bildirme konusunda sorun yaşayacağınızı unutmayın.

Değişken türünün adına erişmek için tek ihtiyacınız olan şey

template <typename T>
const char* get_type_as_string(const T&)
{
    return type_as_string<T>::value;
}

1
Virgül hakkında iyi bir nokta, makroların kötü bir fikir olmasının bir nedeni olduğunu biliyordum ama o zaman bunu düşünmedim!
Greg Hewgill

2
statik sabit char * value = "Wibble"; bu arkadaşı yapamazsın :)
Johannes Schaub - litb

5

İşlev aşırı yüklemesi olmayan bir öncekinden daha genel bir çözüm:

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

Burada Sınıfım kullanıcı tanımlı sınıftır. Burada daha fazla koşul eklenebilir.

Misal:

#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;}

Çıktı:

int
String
MyClass

5

Nick'in yöntemini seviyorum, Tam bir form bu olabilir (tüm temel veri türleri için):

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

2
(i) diğer türler için işe yaramayacaktır (yani hiç jenerik değildir); (ii) yararsız kod şişmesi; (iii) aynı (doğru) ile yapılabilir typeidya da decltype.
edmz

2
Haklısın, ama tüm temel türleri kapsıyor ... ve şu an ihtiyacım olan şey ..
Jahid

2
Bana söyleyebilir misin,
dekltiple

1
Derleme zamanı testi ise, std :: is_same <T, S> ve decltype
komutlarını

4

Ben meydan okurken bir platform bağımsız (umarım) şablon hile ile ne kadar ileri gidebileceğini test etmeye karar verdim.

İsimler derleme zamanında tamamen birleştirilir. (Bu typeid(T).name()da kullanılamadığı için bileşik olmayan türler için açıkça adlar sağlamanız gerekir. Aksi takdirde bunun yerine yer tutucular görüntülenir.)

Örnek kullanım:

TYPE_NAME(int)
TYPE_NAME(void)
// You probably should list all primitive types here.

TYPE_NAME(std::string)

int main()
{
    // A simple case
    std::cout << type_name<void(*)(int)> << '\n';
    // -> `void (*)(int)`

    // Ugly mess case
    // Note that compiler removes cv-qualifiers from parameters and replaces arrays with pointers.
    std::cout << type_name<void (std::string::*(int[3],const int, void (*)(std::string)))(volatile int*const*)> << '\n';
    // -> `void (std::string::*(int *,int,void (*)(std::string)))(volatile int *const*)`

    // A case with undefined types
    //  If a type wasn't TYPE_NAME'd, it's replaced by a placeholder, one of `class?`, `union?`, `enum?` or `??`.
    std::cout << type_name<std::ostream (*)(int, short)> << '\n';
    // -> `class? (*)(int,??)`
    // With appropriate TYPE_NAME's, the output would be `std::string (*)(int,short)`.
}

Kod:

#include <type_traits>
#include <utility>

static constexpr std::size_t max_str_lit_len = 256;

template <std::size_t I, std::size_t N> constexpr char sl_at(const char (&str)[N])
{
    if constexpr(I < N)
        return str[I];
    else
        return '\0';
}

constexpr std::size_t sl_len(const char *str)
{
    for (std::size_t i = 0; i < max_str_lit_len; i++)
        if (str[i] == '\0')
            return i;
    return 0;
}

template <char ...C> struct str_lit
{
    static constexpr char value[] {C..., '\0'};
    static constexpr int size = sl_len(value);

    template <typename F, typename ...P> struct concat_impl {using type = typename concat_impl<F>::type::template concat_impl<P...>::type;};
    template <char ...CC> struct concat_impl<str_lit<CC...>> {using type = str_lit<C..., CC...>;};
    template <typename ...P> using concat = typename concat_impl<P...>::type;
};

template <typename, const char *> struct trim_str_lit_impl;
template <std::size_t ...I, const char *S> struct trim_str_lit_impl<std::index_sequence<I...>, S>
{
    using type = str_lit<S[I]...>;
};
template <std::size_t N, const char *S> using trim_str_lit = typename trim_str_lit_impl<std::make_index_sequence<N>, S>::type;

#define STR_LIT(str) ::trim_str_lit<::sl_len(str), ::str_lit<STR_TO_VA(str)>::value>
#define STR_TO_VA(str) STR_TO_VA_16(str,0),STR_TO_VA_16(str,16),STR_TO_VA_16(str,32),STR_TO_VA_16(str,48)
#define STR_TO_VA_16(str,off) STR_TO_VA_4(str,0+off),STR_TO_VA_4(str,4+off),STR_TO_VA_4(str,8+off),STR_TO_VA_4(str,12+off)
#define STR_TO_VA_4(str,off) ::sl_at<off+0>(str),::sl_at<off+1>(str),::sl_at<off+2>(str),::sl_at<off+3>(str)

template <char ...C> constexpr str_lit<C...> make_str_lit(str_lit<C...>) {return {};}
template <std::size_t N> constexpr auto make_str_lit(const char (&str)[N])
{
    return trim_str_lit<sl_len((const char (&)[N])str), str>{};
}

template <std::size_t A, std::size_t B> struct cexpr_pow {static constexpr std::size_t value = A * cexpr_pow<A,B-1>::value;};
template <std::size_t A> struct cexpr_pow<A,0> {static constexpr std::size_t value = 1;};
template <std::size_t N, std::size_t X, typename = std::make_index_sequence<X>> struct num_to_str_lit_impl;
template <std::size_t N, std::size_t X, std::size_t ...Seq> struct num_to_str_lit_impl<N, X, std::index_sequence<Seq...>>
{
    static constexpr auto func()
    {
        if constexpr (N >= cexpr_pow<10,X>::value)
            return num_to_str_lit_impl<N, X+1>::func();
        else
            return str_lit<(N / cexpr_pow<10,X-1-Seq>::value % 10 + '0')...>{};
    }
};
template <std::size_t N> using num_to_str_lit = decltype(num_to_str_lit_impl<N,1>::func());


using spa = str_lit<' '>;
using lpa = str_lit<'('>;
using rpa = str_lit<')'>;
using lbr = str_lit<'['>;
using rbr = str_lit<']'>;
using ast = str_lit<'*'>;
using amp = str_lit<'&'>;
using con = str_lit<'c','o','n','s','t'>;
using vol = str_lit<'v','o','l','a','t','i','l','e'>;
using con_vol = con::concat<spa, vol>;
using nsp = str_lit<':',':'>;
using com = str_lit<','>;
using unk = str_lit<'?','?'>;

using c_cla = str_lit<'c','l','a','s','s','?'>;
using c_uni = str_lit<'u','n','i','o','n','?'>;
using c_enu = str_lit<'e','n','u','m','?'>;

template <typename T> inline constexpr bool ptr_or_ref = std::is_pointer_v<T> || std::is_reference_v<T> || std::is_member_pointer_v<T>;
template <typename T> inline constexpr bool func_or_arr = std::is_function_v<T> || std::is_array_v<T>;

template <typename T> struct primitive_type_name {using value = unk;};

template <typename T, typename = std::enable_if_t<std::is_class_v<T>>> using enable_if_class = T;
template <typename T, typename = std::enable_if_t<std::is_union_v<T>>> using enable_if_union = T;
template <typename T, typename = std::enable_if_t<std::is_enum_v <T>>> using enable_if_enum  = T;
template <typename T> struct primitive_type_name<enable_if_class<T>> {using value = c_cla;};
template <typename T> struct primitive_type_name<enable_if_union<T>> {using value = c_uni;};
template <typename T> struct primitive_type_name<enable_if_enum <T>> {using value = c_enu;};

template <typename T> struct type_name_impl;

template <typename T> using type_name_lit = std::conditional_t<std::is_same_v<typename primitive_type_name<T>::value::template concat<spa>,
                                                                               typename type_name_impl<T>::l::template concat<typename type_name_impl<T>::r>>,
                                            typename primitive_type_name<T>::value,
                                            typename type_name_impl<T>::l::template concat<typename type_name_impl<T>::r>>;
template <typename T> inline constexpr const char *type_name = type_name_lit<T>::value;

template <typename T, typename = std::enable_if_t<!std::is_const_v<T> && !std::is_volatile_v<T>>> using enable_if_no_cv = T;

template <typename T> struct type_name_impl
{
    using l = typename primitive_type_name<T>::value::template concat<spa>;
    using r = str_lit<>;
};
template <typename T> struct type_name_impl<const T>
{
    using new_T_l = std::conditional_t<type_name_impl<T>::l::size && !ptr_or_ref<T>,
                                       spa::concat<typename type_name_impl<T>::l>,
                                       typename type_name_impl<T>::l>;
    using l = std::conditional_t<ptr_or_ref<T>,
                                 typename new_T_l::template concat<con>,
                                 con::concat<new_T_l>>;
    using r = typename type_name_impl<T>::r;
};
template <typename T> struct type_name_impl<volatile T>
{
    using new_T_l = std::conditional_t<type_name_impl<T>::l::size && !ptr_or_ref<T>,
                                       spa::concat<typename type_name_impl<T>::l>,
                                       typename type_name_impl<T>::l>;
    using l = std::conditional_t<ptr_or_ref<T>,
                                 typename new_T_l::template concat<vol>,
                                 vol::concat<new_T_l>>;
    using r = typename type_name_impl<T>::r;
};
template <typename T> struct type_name_impl<const volatile T>
{
    using new_T_l = std::conditional_t<type_name_impl<T>::l::size && !ptr_or_ref<T>,
                                       spa::concat<typename type_name_impl<T>::l>,
                                       typename type_name_impl<T>::l>;
    using l = std::conditional_t<ptr_or_ref<T>,
                                 typename new_T_l::template concat<con_vol>,
                                 con_vol::concat<new_T_l>>;
    using r = typename type_name_impl<T>::r;
};
template <typename T> struct type_name_impl<T *>
{
    using l = std::conditional_t<func_or_arr<T>,
                                 typename type_name_impl<T>::l::template concat<lpa, ast>,
                                 typename type_name_impl<T>::l::template concat<     ast>>;
    using r = std::conditional_t<func_or_arr<T>,
                                 rpa::concat<typename type_name_impl<T>::r>,
                                             typename type_name_impl<T>::r>;
};
template <typename T> struct type_name_impl<T &>
{
    using l = std::conditional_t<func_or_arr<T>,
                                 typename type_name_impl<T>::l::template concat<lpa, amp>,
                                 typename type_name_impl<T>::l::template concat<     amp>>;
    using r = std::conditional_t<func_or_arr<T>,
                                 rpa::concat<typename type_name_impl<T>::r>,
                                             typename type_name_impl<T>::r>;
};
template <typename T> struct type_name_impl<T &&>
{
    using l = std::conditional_t<func_or_arr<T>,
                                 typename type_name_impl<T>::l::template concat<lpa, amp, amp>,
                                 typename type_name_impl<T>::l::template concat<     amp, amp>>;
    using r = std::conditional_t<func_or_arr<T>,
                                 rpa::concat<typename type_name_impl<T>::r>,
                                             typename type_name_impl<T>::r>;
};
template <typename T, typename C> struct type_name_impl<T C::*>
{
    using l = std::conditional_t<func_or_arr<T>,
                                 typename type_name_impl<T>::l::template concat<lpa, type_name_lit<C>, nsp, ast>,
                                 typename type_name_impl<T>::l::template concat<     type_name_lit<C>, nsp, ast>>;
    using r = std::conditional_t<func_or_arr<T>,
                                 rpa::concat<typename type_name_impl<T>::r>,
                                             typename type_name_impl<T>::r>;
};
template <typename T> struct type_name_impl<enable_if_no_cv<T[]>>
{
    using l = typename type_name_impl<T>::l;
    using r = lbr::concat<rbr, typename type_name_impl<T>::r>;
};
template <typename T, std::size_t N> struct type_name_impl<enable_if_no_cv<T[N]>>
{
    using l = typename type_name_impl<T>::l;
    using r = lbr::concat<num_to_str_lit<N>, rbr, typename type_name_impl<T>::r>;
};
template <typename T> struct type_name_impl<T()>
{
    using l = typename type_name_impl<T>::l;
    using r = lpa::concat<rpa, typename type_name_impl<T>::r>;
};
template <typename T, typename P1, typename ...P> struct type_name_impl<T(P1, P...)>
{
    using l = typename type_name_impl<T>::l;
    using r = lpa::concat<type_name_lit<P1>,
                          com::concat<type_name_lit<P>>..., rpa, typename type_name_impl<T>::r>;
};

#define TYPE_NAME(t) template <> struct primitive_type_name<t> {using value = STR_LIT(#t);};

2
#include <iostream>
#include <typeinfo>
using namespace std;
#define show_type_name(_t) \
    system(("echo " + string(typeid(_t).name()) + " | c++filt -t").c_str())

int main() {
    auto a = {"one", "two", "three"};
    cout << "Type of a: " << typeid(a).name() << endl;
    cout << "Real type of a:\n";
    show_type_name(a);
    for (auto s : a) {
        if (string(s) == "one") {
            cout << "Type of s: " << typeid(s).name() << endl;
            cout << "Real type of s:\n";
            show_type_name(s);
        }
        cout << s << endl;
    }

    int i = 5;
    cout << "Type of i: " << typeid(i).name() << endl;
    cout << "Real type of i:\n";
    show_type_name(i);
    return 0;
}

Çıktı:

Type of a: St16initializer_listIPKcE
Real type of a:
std::initializer_list<char const*>
Type of s: PKc
Real type of s:
char const*
one
two
three
Type of i: i
Real type of i:
int

2

Scott Meyers tarafından Effective Modern C ++ 'da açıklandığı gibi,

Çağrıların std::type_info::namemakul herhangi bir şekilde geri gönderileceği garanti edilmez.

En iyi çözüm, derleyicinin tür kesinti sırasında bir hata mesajı oluşturmasına izin vermektir, örneğin,

template<typename T>
class TD;

int main(){
    const int theAnswer = 32;
    auto x = theAnswer;
    auto y = &theAnswer;
    TD<decltype(x)> xType;
    TD<decltype(y)> yType;
    return 0;
}

Sonuç, derleyicilere bağlı olarak,

test4.cpp:10:21: error: aggregate TD<int> xType has incomplete type and cannot be defined TD<decltype(x)> xType;

test4.cpp:11:21: error: aggregate TD<const int *> yType has incomplete type and cannot be defined TD<decltype(y)> yType;

Bu yüzden, o tanımak x'ın türüdür int, y' ın türüdürconst int*


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.