例如:
int a = 12;
cout << typeof(a) << endl;
预期的输出:
int
例如:
int a = 12;
cout << typeof(a) << endl;
预期的输出:
int
当前回答
对于一些不同的东西,这里有一个类型的“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;
}
其他回答
Try:
#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*>'
#include <iostream>
#include <typeinfo>
using namespace std;
#define show_type_name(_t) \
system(("echo " + string(typeid(_t).name()) + " | c++filt -t").c_str())
int main() {
auto a = {"one", "two", "three"};
cout << "Type of a: " << typeid(a).name() << endl;
cout << "Real type of a:\n";
show_type_name(a);
for (auto s : a) {
if (string(s) == "one") {
cout << "Type of s: " << typeid(s).name() << endl;
cout << "Real type of s:\n";
show_type_name(s);
}
cout << s << endl;
}
int i = 5;
cout << "Type of i: " << typeid(i).name() << endl;
cout << "Real type of i:\n";
show_type_name(i);
return 0;
}
输出:
Type of a: St16initializer_listIPKcE
Real type of a:
std::initializer_list<char const*>
Type of s: PKc
Real type of s:
char const*
one
two
three
Type of i: i
Real type of i:
int
对于那些还在访问的人,我最近也遇到了同样的问题,并决定根据这篇文章的答案编写一个小型库。它提供了constexpr类型名称和类型索引,并且在Mac, Windows和Ubuntu上进行了测试。
库代码在这里:https://github.com/TheLartians/StaticTypeInfo
一个没有函数重载的更通用的解决方案:
template<typename T>
std::string TypeOf(T){
std::string Type="unknown";
if(std::is_same<T,int>::value) Type="int";
if(std::is_same<T,std::string>::value) Type="String";
if(std::is_same<T,MyClass>::value) Type="MyClass";
return Type;}
这里的MyClass是用户定义的类。这里还可以添加更多的条件。
例子:
#include <iostream>
class MyClass{};
template<typename T>
std::string TypeOf(T){
std::string Type="unknown";
if(std::is_same<T,int>::value) Type="int";
if(std::is_same<T,std::string>::value) Type="String";
if(std::is_same<T,MyClass>::value) Type="MyClass";
return Type;}
int main(){;
int a=0;
std::string s="";
MyClass my;
std::cout<<TypeOf(a)<<std::endl;
std::cout<<TypeOf(s)<<std::endl;
std::cout<<TypeOf(my)<<std::endl;
return 0;}
输出:
int
String
MyClass