표준 C ++에서 변수 유형을 인쇄 할 수 있습니까? 다음과 같습니다. int a = 12; cout

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

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

예상 출력 :

int



답변

매우 오래된 질문으로 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의 상수를 업데이트했습니다.

최신 정보:

최신 공식에서 읽을 수없는 마법 번호를 제거하는 이 재 작성을 아래 에서 확인하십시오 .


답변

시험:

#include <typeinfo>

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

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


답변

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

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


답변

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

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

#include <iostream>
#include <typeinfo>

using namespace std;

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


답변

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


답변

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 () 속성을 여전히 로그 / 디버그 목적으로 사용할 수 있습니다


답변

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

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

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