질문 : 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
'프로그래밍 언어 > C++' 카테고리의 다른 글
new를 사용하여 C ++에서 2차원 배열을 선언하는 방법 (0) | 2021.07.06 |
---|---|
__name__ == “__main__”: 하면 어떻게 될까? (0) | 2021.07.02 |
정렬되지 않은 배열을 처리하는 것보다 정렬 된 배열을 처리하는 것이 더 빠른 이유 (0) | 2021.07.01 |
C ++ 코드 파일 확장자 .cc와 .cpp의 차이점 (0) | 2021.06.30 |
C ++에서 구조체와 클래스의 차이점 (0) | 2021.06.29 |