표준 C ++에서 변수 유형을 인쇄 할 수 있습니까?


393

예를 들면 다음과 같습니다.

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

예상 출력 :

int

2
다음은 Howard의 긴 형식 솔루션에 대한 요약이지만 이단적인 한 줄 매크로로 구현되었습니다 #define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL). 당신이 크로스 플랫폼 지원이 필요한 경우 : 사용 #ifdef, #else, #endifMSVC와 같은 다른 플랫폼에 대한 하나의 매크로를 제공합니다.
Trevor Boyd Smith

보다 명확한 사람이 읽을 수있는 요구 사항 : stackoverflow.com/questions/12877521/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

3
디버깅에만이 기능을 사용하는 경우 고려할 수 있습니다 template<typename T> void print_T() { std::cout << __PRETTY_FUNCTION__ << '\n'; }. 그런 다음 예 print_T<const int * const **>();를 사용 하면 void print_T() [T = const int *const **]런타임에 인쇄 하고 모든 한정자를 유지합니다 (GCC 및 Clang에서 작동).
Henri Menke

@Henri __PRETTY_FUNCTION__는 표준 C ++이 아닙니다 (요구 사항은 질문 제목에 있음).
Toby Speight

답변:


505

매우 오래된 질문으로 C ++ 11 업데이트 : C ++에서 변수 유형 인쇄.

받아 들여지고 좋은 대답은을 사용하는 것입니다 typeid(a).name(). 여기서 a변수 이름입니다.

이제 C ++ 11 decltype(x)에는 표현식이 유형으로 바뀔 수 있습니다. 그리고 decltype()매우 흥미로운 규칙이 있습니다. 예를 들어 decltype(a)decltype((a)) 일반적으로 다른 유형의 것 (좋은 그 이유를 한 번에 이해할 수있는 이유에 대한 노출).

우리의 신뢰 typeid(a).name() 이이 용감한 새로운 세상을 탐험하는 데 도움이됩니까?

아니.

그러나 그 도구는 그렇게 복잡하지 않습니다. 그리고이 질문에 대한 답으로 사용하고있는 도구입니다. 이 새로운 도구를 비교하고 대조하겠습니다 typeid(a).name(). 이 새로운 도구는 실제로 위에 구축되었습니다 typeid(a).name().

근본적인 문제 :

typeid(a).name()

cv 한정자, 참조 및 lvalue / rvalue-ness를 버립니다. 예를 들면 다음과 같습니다.

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

나에게 출력 :

i

그리고 MSVC 출력에 대해 추측하고 있습니다.

int

즉, const사라졌다. 이것은 QOI (Quality Of Implementation) 문제가 아닙니다. 표준은이 동작을 의무화합니다.

아래 권장 사항은 다음과 같습니다.

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 솔루션

demangle 유형에 대한 답변에서 ipapadop__cxa_demangle권장하는 비 MSVC 플랫폼을 사용 하고 있습니다. 그러나 MSVC에서 나는 이름을 요구하지 않을 것을 신뢰 하고 있습니다. 그리고이 핵심은 cv 한정자와 입력 유형에 대한 참조를 감지, 복원 및보고하는 간단한 테스트를 둘러싼 것입니다.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. (표현식에는 참조 유형이 없지만, 규칙에 따라 decltypelvalue 참조가있는 lvalue 표현식을 나타냅니다).

따라서이 도구는 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

즉 모든 참조 및 cv 한정자가 제거됩니다.

C ++ 14 업데이트

당신이 문제에 대한 해결책을 찾았다 고 생각할 때 누군가는 항상 아무데도 나오지 않고 훨씬 더 나은 방법을 보여줍니다. :-)

Jamboree 의이 답변 은 컴파일 타임에 C ++ 14에서 유형 이름을 얻는 방법을 보여줍니다. 몇 가지 이유로 훌륭한 솔루션입니다.

  1. 컴파일 시간입니다!
  2. 라이브러리 자체 (std :: lib) 대신 컴파일러 자체에서 작업을 수행 할 수 있습니다. 이는 최신 언어 기능 (예 : 람다)에 대한보다 정확한 결과를 의미합니다.

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
}

이 코드는 constexpr고대 C ++ 11에 여전히 갇혀 있으면 자동 백 오프됩니다 . C ++ 98 / 03으로 동굴 벽에 그림을 그리는 경우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
VS 14 CTP는 올바른 유형을 인쇄했지만 한 #include <iostream>줄만 추가하면 됩니다.
Max Galkin

3
왜 template <typename T> std :: string type_name ()입니까? 왜 타입을 인수로 전달하지 않습니까?
moonman239

2
필자의 이론적 근거는 때로는 유형 (예 : 추론 된 템플릿 매개 변수) 있었으며 유형을 얻기 위해 그 중 하나를 인위적으로 구성하고 싶지는 않았다고 생각합니다 declval.
Howard Hinnant

5
@AngelusMortis : 영어는 C ++ 코드에 비해 모호하고 모호하기 때문에 관심있는 특정 유형과 관심있는 특정 컴파일러를 사용하여 테스트 사례에 복사 / 붙여 넣기하고 더 많은 내용으로 다시 작성하는 것이 좋습니다. 결과가 놀랍거나 불만족스러운 경우 세부 사항.
Howard Hinnant

3
@HowardHinnant std::string_view대신 사용할 수 static_string있습니까?
Lyberta

231

시험:

#include <typeinfo>

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

이것이 작동하려면 컴파일러 옵션에서 RTTI를 활성화해야 할 수도 있습니다. 또한이 결과는 컴파일러에 따라 다릅니다. 원시 유형 이름 또는 이름 맹 글링 기호 또는 그 사이의 다른 이름 일 수 있습니다.


4
name () 함수가 반환 한 문자열이 구현 정의 된 이유는 무엇입니까?
소멸자

4
@PravasiMeet 내가 아는 한 아무런 이유가 없습니다. 위원회는 단순히 컴파일러 구현 자들을 특정한 기술적 방향으로 강요하기를 원치 않았습니다.
Konrad Rudolph

2
RTTI를 활성화하는 데 사용할 수있는 플래그가 있습니까? 아마도 당신은 당신의 대답을 포함시킬 수있을 것입니다.
Jim

4
@Destructor 표준화 된 이름 맹 글링 (mangling) 형식을 제공하면 두 개의 서로 다른 컴파일러가 빌드 한 바이너리 간 상호 운용성이 가능하고 안전하지 않다는 인상을 줄 수 있습니다. C ++에는 표준 ABI가 없으므로 표준 이름 맹 글링 체계는 의미가 없으며 잠재적으로 오도되고 위험 할 수 있습니다.
Elkvis

1
@Jim 컴파일러 플래그의 섹션은 답변 자체보다 훨씬 더 길다. GCC는 기본적으로 "-fno-rtti"로 컴파일하므로 다른 컴파일러는 그렇지 않을 수도 있지만 컴파일러 플래그에 대한 표준은 없습니다.
kfsone

82

매우 추악하지만 컴파일 시간 정보 만 원하는 경우 트릭을 수행합니다 (예 : 디버깅).

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
c ++만이 이것을 어렵게 만들 수 있습니다 (컴파일 타임에 자동 변수 유형 인쇄). C ++ 만.
Karl Pickett

3
@KarlP는 약간 복잡하지만 공정합니다. :) auto testVar = std::make_tuple(1, 1.0, "abc"); decltype(testVar)::foo = 1;
NickV

VC ++ 17에서는 forwarding-reference 매개 변수가있는 템플리트 함수 및 오브젝트 이름이 std :: forward로 래핑 된 경우에도 rvalue 참조를 일반 참조로 줄입니다.
Jive Dadson

새로운 바퀴를 만들지 않고도 유형에 도달 할 수있었습니다!
Steven Eckhoff

1
이 기술은 또한 Modern Modern C ++의 "항목 4 : 추론 된 유형을 보는 방법을 알고 있습니다"
lenkite

54

포함하는 것을 잊지 마십시오 <typeinfo>

당신이 말하는 것은 런타임 유형 식별이라고 생각합니다. 를 수행하여 위를 달성 할 수 있습니다.

#include <iostream>
#include <typeinfo>

using namespace std;

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

36

Howard 의 솔루션 에 따르면 , 마법의 숫자를 원하지 않으면 이것이 표현하기에 좋은 방법이며 직관적으로 보입니다.

#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
이것은 과거의 몇 가지 C ++ 버전에 대한 노력을 짧고 감미로운 것으로 크게 나눕니다. +1.
einpoklum

1
이것도 내가 가장 좋아하는 것입니다!
Howard Hinnant

1
다음은 접미사 / 접두사를 자동으로 감지하는 비슷한 기능입니다. stackoverflow.com/questions/1055452/…
HolyBlackCat

22

C ++의 RTTI 기능으로 생성 된 이름은 이식 가능 하지 않습니다 . 예를 들어, 클래스

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

이름은 다음과 같습니다.

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

따라서이 정보를 직렬화에 사용할 수 없습니다. 그러나 여전히 typeid (a) .name () 속성을 여전히 로그 / 디버그 목적으로 사용할 수 있습니다


19

템플릿을 사용할 수 있습니다.

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

위의 예에서 유형이 일치하지 않으면 "알 수 없음"이 인쇄됩니다.


3
반바지와 문자에 대해 "int"로 인쇄되지 않습니까? 그리고 복식에 대한 "부동"?
gartenriese

1
@gartenriese 전문화에는 단점이 없습니다. 왜냐하면 double특수화를 사용하기 위해 암시 적 타입 변환보다는 특수화되지 않은 템플릿 함수 버전을 컴파일 할 것입니다 : cpp.sh/2wzc
chappjc

1
@chappjc : 솔직히 내가 왜 그렇게 물 었는지 모르겠지만, 지금은 분명합니다. 어쨌든 1 년 된 질문에 답해 주셔서 감사합니다!
gartenriese

2
@gartenriese 나는 많은 것을 생각했지만 "인터넷"은 어느 시점에서 같은 질문을 가질 수 있습니다.
chappjc

18

언급 한 것처럼 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;

}


10

이를 위해 특성 클래스를 사용할 수 있습니다. 다음과 같은 것 :

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

그만큼 DECLARE_TYPE_NAME 당신이 필요로 기대하는 모든 유형이 특성 클래스를 선언하는 당신의 인생을 더 쉽게하기 위해 존재 정의합니다.

이것은 typeid출력을 제어하기 때문에 관련된 솔루션보다 더 유용 할 수 있습니다 . 예를 들어 컴파일러에서 typeidfor long long를 사용 하면 "x"가 표시됩니다.


10

C ++ 11에는 decltype이 있습니다. 표준 c ++에서는 decltype을 사용하여 선언 된 정확한 유형의 변수를 표시 할 방법이 없습니다. 우리는 boost typeindex (즉 type_id_with_cvr, cvr 은 const, volatile, reference를 나타냄)를 사용하여 아래와 같이 유형을 인쇄 할 수 있습니다.

#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
도우미 함수를 사용하는 것이 더 간단 template<typename T> void print_type(T){cout << "type T is: "<< type_id_with_cvr<T>().pretty_name()<< '\n';}
할까요?

6

-t (유형) 옵션과 함께 c ++ filt를 사용하여 유형 이름을 지정할 수도 있습니다.

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

리눅스에서만 테스트되었습니다.


1
못 생겼지 만 내가 원하는 것을 위해 할 것입니다. 다른 솔루션보다 훨씬 작습니다. Mac btw에서 작동합니다.
Marco Luglio

6

Howard Hinnant 는 매직 넘버를 사용하여 형식 이름을 추출했습니다. 康 桓 瑋 제안 문자열 접두사 및 접미사. 그러나 접두사 / 접미사는 계속 변경됩니다. "probe_type"을 사용하면 type_name은 자동으로 "probe_type"의 접두사 및 접미사 크기를 계산하여 유형 이름을 추출합니다.

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

산출

gcc 10.0.0 20190919 완드 박스 :

 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 완드 박스 :

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

VS 2019 버전 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 (typeid)와 관련된 다른 답변은 다음과 같은 경우 원하는 것입니다.

  • 메모리 오버 헤드를 감당할 수 있습니다 (일부 컴파일러에서는 상당 할 수 있음)
  • 컴파일러가 반환하는 클래스 이름이 유용합니다

대안은 (Greg Hewgill의 답변과 유사) 컴파일 타임 특성 테이블을 작성하는 것입니다.

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

매크로에서 선언을 줄 바꿈하면 쉼표로 인해 둘 이상의 매개 변수 (예 : std :: map)를 사용하는 템플릿 유형의 이름을 선언하는 데 문제가있을 수 있습니다.

변수 유형의 이름에 액세스하려면 필요한 것은

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

1
쉼표에 대한 좋은 점은 매크로가 좋지 않은 이유가 있다는 것을 알고 있었지만 당시에는 생각하지 않았습니다!
Greg Hewgill

2
static const char * value = "Wibble"; 당신은 그 친구를 할 수 없습니다 :)
Johannes Schaub-litb

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

5

나는 Nick의 방법을 좋아합니다. 완전한 형식은 다음과 같습니다 (모든 기본 데이터 유형).

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) 다른 유형에서는 작동하지 않습니다 (즉, 전혀 제네릭이 아님). (ii) 쓸모없는 코드 팽창; (ⅲ) 같은 (정확하게)으로 수행 될 수 typeid또는 decltype.
edmz

2
당신은 옳습니다. 그러나 그것은 모든 기본 유형을 다루고 있습니다. 그리고 그것은 바로 지금 필요한 것입니다.
Jahid

2
당신은 decltype으로 어떻게
하겠습니까?

1
컴파일 타임 테스트 인 경우 std :: is_same <T, S> 및 decltype을 사용하여 T와 S를 얻을 수 있습니다.
edmz

4

내가 도전을 할 때 플랫폼 독립적 인 (바람직한) 템플릿 속임수로 얼마나 멀리 갈 수 있는지 테스트하기로 결정했습니다.

이름은 컴파일 타임에 완전히 조립됩니다. (어떤 의미typeid(T).name() 것은 사용할 수 없으므로 비 복합 유형의 이름을 명시 적으로 제공해야합니다. 그렇지 않으면 자리 표시자가 대신 표시됩니다.)

사용법 예 :

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)`.
}

암호:

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

산출:

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

효과적인 현대 C ++에서 Scott Meyers가 설명했듯이

에 대한 전화 std::type_info::name는 현명한 반환을 보장하지 않습니다.

가장 좋은 해결책은 예를 들어, 형식을 추론하는 동안 컴파일러가 오류 메시지를 생성하도록하는 것입니다.

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

컴파일러에 따라 결과는 다음과 같습니다.

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;

따라서, 우리는 것을 알게 x의 유형이 int, y의 유형입니다const int*


0

여전히 방문하는 사람에게는 최근에 같은 문제가 있었고이 게시물의 답변을 기반으로 작은 라이브러리를 작성하기로 결정했습니다. constexpr 유형 이름과 유형 색인을 제공하며 Mac, Windows 및 Ubuntu에서 테스트됩니다.

라이브러리 코드는 다음과 같습니다. https://github.com/TheLartians/StaticTypeInfo

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.