例如:
int a = 12; cout << typeof(a) << endl;
预期输出:
int
C11 更新了一个非常老的问题:在 C 中打印变量类型。
公认的(也是好的)答案是使用typeid(a).name(),其中a是变量名。
typeid(a).name()
a
现在在 C++11 中我们有了decltype(x),它可以将表达式转换为类型。并decltype()带有自己的一套非常有趣的规则。例如decltype(a),并且decltype((a))通常会是不同的类型(一旦这些原因暴露出来,出于良好和可理解的原因)。
decltype(x)
decltype()
decltype(a)
decltype((a))
我们的信任会typeid(a).name()帮助我们探索这个勇敢的新世界吗?
不。
但是那个工具并没有那么复杂。这就是我用来回答这个问题的工具。我会将这个新工具与typeid(a).name(). 而这个新工具实际上是建立在typeid(a).name().
根本问题:
丢弃 cv 限定符、引用和左值/右值。例如:
const int ci = 0; std::cout << typeid(ci).name() << '\n';
对我来说输出:
i
我在猜测 MSVC 输出:
即const消失了。这不是 QOI(实施质量)问题。该标准规定了这种行为。
const
我在下面推荐的是:
template <typename T> std::string type_name();
这将像这样使用:
const int ci = 0; std::cout << type_name<decltype(ci)>() << '\n';
int const
<disclaimer>我没有在 MSVC 上测试过这个。</disclaimer> 但我欢迎那些这样做的人提供反馈。
<disclaimer>
</disclaimer>
C++11 解决方案
我正在使用ipapadop在他对 demangle 类型的回答中__cxa_demangle推荐的非 MSVC 平台。但在 MSVC 上,我相信可以解开名称(未经测试)。这个核心包含一些简单的测试,检测、恢复和报告 cv-qualifiers 和对输入类型的引用。typeid
__cxa_demangle
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的“类型” 。(表达式从不具有引用类型,但作为约定,decltype表示具有左值引用的左值表达式)。
decltype(i)
decltype((i))
decltype
decltype因此,除了探索和调试您自己的代码之外,该工具还是一个很好的学习工具。
相反,如果我只是在 上构建它typeid(a).name(),而不添加丢失的 cv-qualifiers 或引用,则输出将是:
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 更新
就在您认为自己已经找到了解决问题的方法时,总会有人突然冒出来,向您展示更好的方法。:-)
显示了如何在编译时获取C++14 中的类型名称。这是一个绝妙的解决方案,原因如下:
并没有完全说明 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如果您仍然停留在古老的 C11 中,此代码将自动退避。如果你用 C98/03 在洞壁上作画,noexcept也会被牺牲掉。
constexpr
noexcept
C++17 更新
在下面的评论中,指出新的std::string_view可以替换static_string:
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 <string_view> template <typename T> constexpr auto type_name() { std::string_view name, prefix, suffix; #ifdef __clang__ name = __PRETTY_FUNCTION__; prefix = "auto type_name() [T = "; suffix = "]"; #elif defined(__GNUC__) name = __PRETTY_FUNCTION__; prefix = "constexpr auto type_name() [with T = "; suffix = "]"; #elif defined(_MSC_VER) name = __FUNCSIG__; prefix = "auto __cdecl type_name<"; suffix = ">(void)"; #endif name.remove_prefix(prefix.size()); name.remove_suffix(suffix.size()); return name; }
演示。