例如:
int a = 12;
cout << typeof(a) << endl;
预期的输出:
int
例如:
int a = 12;
cout << typeof(a) << endl;
预期的输出:
int
当前回答
如前所述,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;
}
其他回答
对于一些不同的东西,这里有一个类型的“To English”转换,解构每个限定符、范围、参数等等,递归地构建描述类型的字符串,我认为“演绎这个”建议将有助于减少许多特殊化。无论如何,这是一个有趣的晨练,尽管过度膨胀。:)
struct X {
using T = int *((*)[10]);
T f(T, const unsigned long long * volatile * );
};
int main() {
std::cout << describe<decltype(&X::f)>() << std::endl;
}
输出:
pointer to member function of class 1X taking (pointer to array[10]
of pointer to int, pointer to volatile pointer to const unsigned
long long), and returning pointer to array[10] of pointer to int
代码如下: https://godbolt.org/z/7jKK4or43
注:最新版本在我的github: https://github.com/cuzdav/type_to_string
// Print types as strings, including functions, member
#include <type_traits>
#include <typeinfo>
#include <string>
#include <utility>
namespace detail {
template <typename T> struct Describe;
template <typename T, class ClassT>
struct Describe<T (ClassT::*)> {
static std::string describe();
};
template <typename RetT, typename... ArgsT>
struct Describe<RetT(ArgsT...)> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...)> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) const> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) volatile> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) noexcept> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) const volatile> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) const noexcept> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) volatile noexcept> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) const volatile noexcept> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...)&> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) const &> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) volatile &> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) & noexcept> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) const volatile &> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) const & noexcept> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) volatile & noexcept> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) const volatile & noexcept> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) &&> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) const &&> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) volatile &&> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) && noexcept> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) const volatile &&> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) const && noexcept> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) volatile && noexcept> {
static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT>
struct Describe<RetT(ClassT::*)(ArgsT...) const volatile && noexcept> {
static std::string describe();
};
template <typename T>
std::string describe()
{
using namespace std::string_literals;
auto terminal = [&](char const * desc) {
return desc + " "s + typeid(T).name();
};
if constexpr(std::is_const_v<T>) {
return "const " + describe<std::remove_const_t<T>>();
}
else if constexpr(std::is_volatile_v<T>) {
return "volatile " + describe<std::remove_volatile_t<T>>();
}
else if constexpr (std::is_same_v<bool, T>) {
return "bool";
}
else if constexpr(std::is_same_v<char, T>) {
return "char";
}
else if constexpr(std::is_same_v<signed char, T>) {
return "signed char";
}
else if constexpr(std::is_same_v<unsigned char, T>) {
return "unsigned char";
}
else if constexpr(std::is_unsigned_v<T>) {
return "unsigned " + describe<std::make_signed_t<T>>();
}
else if constexpr(std::is_void_v<T>) {
return "void";
}
else if constexpr(std::is_integral_v<T>) {
if constexpr(std::is_same_v<short, T>)
return "short";
else if constexpr(std::is_same_v<int, T>)
return "int";
else if constexpr(std::is_same_v<long, T>)
return "long";
else if constexpr(std::is_same_v<long long, T>)
return "long long";
}
else if constexpr(std::is_same_v<float, T>) {
return "float";
}
else if constexpr(std::is_same_v<double, T>) {
return "double";
}
else if constexpr(std::is_same_v<long double, T>) {
return "long double";
}
else if constexpr(std::is_same_v<std::nullptr_t, T>) {
return "nullptr_t";
}
else if constexpr(std::is_class_v<T>) {
return terminal("class");
}
else if constexpr(std::is_union_v<T>) {
return terminal("union");
}
else if constexpr(std::is_enum_v<T>) {
std::string result;
if (!std::is_convertible_v<T, std::underlying_type_t<T>>) {
result += "scoped ";
}
return result + terminal("enum");
}
else if constexpr(std::is_pointer_v<T>) {
return "pointer to " + describe<std::remove_pointer_t<T>>();
}
else if constexpr(std::is_lvalue_reference_v<T>) {
return "lvalue-ref to " + describe<std::remove_reference_t<T>>();
}
else if constexpr(std::is_rvalue_reference_v<T>) {
return "rvalue-ref to " + describe<std::remove_reference_t<T>>();
}
else if constexpr(std::is_bounded_array_v<T>) {
return "array[" + std::to_string(std::extent_v<T>) + "] of " +
describe<std::remove_extent_t<T>>();
}
else if constexpr(std::is_unbounded_array_v<T>) {
return "array[] of " + describe<std::remove_extent_t<T>>();
}
else if constexpr(std::is_function_v<T>) {
return Describe<T>::describe();
}
else if constexpr(std::is_member_object_pointer_v<T>) {
return Describe<T>::describe();
}
else if constexpr(std::is_member_function_pointer_v<T>) {
return Describe<T>::describe();
}
}
template <typename RetT, typename... ArgsT>
std::string Describe<RetT(ArgsT...)>::describe() {
std::string result = "function taking (";
((result += detail::describe<ArgsT>(", ")), ...);
return result + "), returning " + detail::describe<RetT>();
}
template <typename T, class ClassT>
std::string Describe<T (ClassT::*)>::describe() {
return "pointer to member of " + detail::describe<ClassT>() +
" of type " + detail::describe<T>();
}
struct Comma {
char const * sep = "";
std::string operator()(std::string const& str) {
return std::exchange(sep, ", ") + str;
}
};
enum Qualifiers {NONE=0, CONST=1, VOLATILE=2, NOEXCEPT=4, LVREF=8, RVREF=16};
template <typename RetT, typename ClassT, typename... ArgsT>
std::string describeMemberPointer(Qualifiers q) {
std::string result = "pointer to ";
if (NONE != (q & CONST)) result += "const ";
if (NONE != (q & VOLATILE)) result += "volatile ";
if (NONE != (q & NOEXCEPT)) result += "noexcept ";
if (NONE != (q & LVREF)) result += "lvalue-ref ";
if (NONE != (q & RVREF)) result += "rvalue-ref ";
result += "member function of " + detail::describe<ClassT>() + " taking (";
Comma comma;
((result += comma(detail::describe<ArgsT>())), ...);
return result + "), and returning " + detail::describe<RetT>();
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...)>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(NONE);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) const>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(CONST);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) noexcept>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) volatile>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(VOLATILE);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) volatile noexcept>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(VOLATILE | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) const volatile>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(CONST | VOLATILE);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) const noexcept>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(CONST | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) const volatile noexcept>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(CONST | VOLATILE | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) &>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(LVREF);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) const &>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(LVREF | CONST);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) & noexcept>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(LVREF | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) volatile &>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(LVREF | VOLATILE);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) volatile & noexcept>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(LVREF | VOLATILE | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) const volatile &>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(LVREF | CONST | VOLATILE);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) const & noexcept>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(LVREF | CONST | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) const volatile & noexcept>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(LVREF | CONST | VOLATILE | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...)&&>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(RVREF);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) const &&>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(RVREF | CONST);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) && noexcept>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(RVREF | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) volatile &&>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(RVREF | VOLATILE);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) volatile && noexcept>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(RVREF | VOLATILE | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) const volatile &&>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(RVREF | CONST | VOLATILE);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) const && noexcept>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(RVREF | CONST | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT>
std::string Describe<RetT(ClassT::*)(ArgsT...) const volatile && noexcept>::describe() {
return describeMemberPointer<RetT, ClassT, ArgsT...>(RVREF | CONST | VOLATILE | NOEXCEPT);
}
} // detail
///////////////////////////////////
// Main function
///////////////////////////////////
template <typename T>
std::string describe() {
return detail::describe<T>();
}
///////////////////////////////////
// Sample code
///////////////////////////////////
#include <iostream>
struct X {
using T = int *((*)[10]);
T f(T, const unsigned long long * volatile * );
};
int main() {
std::cout << describe<decltype(&X::f)>() << std::endl;
}
涉及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;
}
如前所述,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;
}
您可以使用模板。
template <typename T> const char* typeof(T&) { return "unknown"; } // default
template<> const char* typeof(int&) { return "int"; }
template<> const char* typeof(float&) { return "float"; }
在上面的例子中,当类型不匹配时,它将打印“unknown”。
我喜欢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*"; }