프로그래밍 언어/C++

C ++에서 변수 데이터 타입을 출력 하는 법

Rateye 2021. 7. 1. 10:10
728x90
반응형

 

질문 : C ++에서 변수 데이터 타입을 출력 하는 법

예를 들면 :

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

예상 출력 :

int
답변

아주 오래된 질문에 대한 C ++ 11 업데이트 : C ++에서 변수 유형 인쇄.

허용되는 (그리고 좋은) 대답은 typeid(a).name() a 입니다. 여기서 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 솔루션

내가 사용하고 __cxa_demangle 에 의해 추천 비 MSVC 플랫폼 ipapadop 디맹 글링 유형에 대한 그의 대답. 그러나 MSVC에서는 typeid 를 신뢰하여 이름을 엉망으로 만듭니다. 그리고이 코어는 cv 한정자 및 입력 유형에 대한 참조를 감지, 복원 및보고하는 몇 가지 간단한 테스트를 둘러싸고 있습니다.

#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 은 lvalue 참조가있는 lvalue 표현식을 나타냅니다.

따라서이 도구는 자신의 코드를 탐색하고 디버깅하는 것 외에도 decltype 에 대해 배우는 데 탁월한 도구입니다.

반대로 잃어버린 cv 한정자 또는 참조를 다시 추가하지 않고 typeid(a).name()

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에서 유형 이름을 얻는 방법을 보여줍니다. 다음과 같은 몇 가지 이유로 훌륭한 솔루션입니다.

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 ++ 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의 상수를 업데이트했습니다.

내 최신 공식에서 읽을 수없는 매직 넘버를 제거하는 이 재 작성을 아래 에서 확인하십시오.

출처 : https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c
728x90
반응형