我想知道在C++中是否有可能以字符串形式检索类的名称,而无需硬编码为变量或getter。 我知道这些信息都不会在运行时使用,因此不可用,但是否有任何宏可以创建此功能?
编辑:需要注意的是,我实际上正在尝试检索派生类的名称,并且我正在使用Visual C++ 2008 Express Edition。
我想知道在C++中是否有可能以字符串形式检索类的名称,而无需硬编码为变量或getter。 我知道这些信息都不会在运行时使用,因此不可用,但是否有任何宏可以创建此功能?
编辑:需要注意的是,我实际上正在尝试检索派生类的名称,并且我正在使用Visual C++ 2008 Express Edition。
typeid
。#include <typeinfo>
std::cout << typeid(obj).name() << "\n";
然而,该类型名称并非标准化的,可能因编译器不同(甚至是同一编译器的不同版本)而有所差异,并且通常不易于人类阅读,因为它被mangled了。
在GCC和clang(带有libstdc++和libc++)上,您可以使用__cxa_demangle
函数对名称进行反演(在MSVC上似乎不需要进行名称反演):
#include <cxxabi.h>
#include <cstdlib>
#include <memory>
#include <string>
std::string demangle(char const* mangled) {
auto ptr = std::unique_ptr<char, decltype(& std::free)>{
abi::__cxa_demangle(mangled, nullptr, nullptr, nullptr),
std::free
};
return {ptr.get()};
}
这仍然不一定是一个可读的名称 — 例如,std::string
是实际类型的类型名称,在当前的 libstdc++ 中,它的完整类型名称是 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
;相比之下,在当前的 libc++ 中,它是 std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >
。 "美化" 类型别名并不是一件容易的事情。
typeid
仅适用于多态类型,这可能源于与RTTI功能类似。 - 事实上,在静态类型上使用typeid
不需要也不使用RTTI。该运算符在编译时计算,并将结果编译进去(严格来说,这是一项实现细节,但这是唯一合理的实现)。 - Konrad Rudolph如果你只是想检查它是否属于某个类,那么
typeid(obj) == typeid(CSubClass)
无论如何都会起作用,不受实现方式的影响。
否则,一种方便的方法是声明:
virtual const char* classname() { return "CMyClass";}
并在每个子类中实现。
typeid()
返回一个指针typeinfo*
。如果两个typeinfo*
指针相等,则它们引用相同的类型,但如果它们不相等,则它们仍然可能引用相同的类型。这就是为什么有一个带有适当语义的std::type_index
类。只有当两种类型相等时,std::type_index(typeid(obj)) == std::type_index(typeid(CSubClass))
才会为真。 - MSalterstypeid(obj).name()
会始终返回变量被声明时的类型,而不是实际对象所属的类。如果变量 obj
被赋值为其父类的子类的实例,则 typeid 不会揭示这一点。
*
就可以解决问题。 - Mark RansomBaseClass* ptr = new SubClass;
之后,我发现typeid(ptr).name()
返回class BaseClass *
,而typeid(*ptr).name()
则返回class SubClass
。 - Jonathan Lidbeck使用C++17和第三方库,现在可以获取类的名称,例如
#include <iostream>
#include "nameof.hpp"
namespace test {
class Object {};
}
int main() {
constexpr auto obj_name = nameof::nameof_type<test::Object>();
std::cout << obj_name << std::endl;
// this prints "test::Object"
}
这只使用编译时信息,因此可以是constexpr
。请注意,它不是可移植的;例如,英特尔的编译器不受支持。
关于这个问题,
在Windows 10上使用Visual Studio 2019(v142)进行测试。
#include <iostream>
#include <typeinfo>
#include <string>
/**
@author blongho
@fn template<typename Object> std::string classNameOf()
@brief Determine the class name of an object
@tparam Object Type of the object.
@returns A name of the class
@date 2019-09-06
*/
template<typename Object>
std::string classNameOf() {
std::string name = typeid(Object).name(); //* user defined types gives "class Type"*\
size_t spacePosition = name.find_first_of(" ");
if (spacePosition != std::string::npos) {
return name.substr(spacePosition + 1, name.length());
}
return name; // mostly primitive types
}
class Person {
private:
/* data */
public:
Person() {};
~Person() {};
};
class Data
{
private:
/* data */
public:
Data() {};
~Data() {};
};
struct Type {};
int main() {
std::cout << "Class name of Person() is \"" << classNameOf<Person>() << "\"\n";
std::cout << "Class name of Data() is \"" << classNameOf<Data>() << "\"\n";
std::cout << "Class name of Type() is \"" << classNameOf<Type>() << "\"\n";
std::cout << "Class name of double is \"" << classNameOf<double>() << "\"\n";
std::cout << "Class name of std::string is \"" << classNameOf<std::string>() << "\"\n";
std::cout << "Class name of int is \"" << classNameOf<int>() << "\"\n";
std::cout << "Class name of float is \"" << classNameOf<float>() << "\"\n";
std::cout << "Class name of char is \"" << classNameOf<char>() << "\"\n";
return 0;
}
输出
Class name of Person() is "Person"
Class name of Data() is "Data"
Class name of Type() is "Type"
Class name of double is "double"
Class name of std::string is "std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >"
Class name of int is "int"
Class name of float is "float"
Class name of char is "char"
g++ -o test src/main.cpp
./test
Class name of Person() is "6Person"
Class name of Data() is "4Data"
Class name of Type() is "4Type"
Class name of double is "d"
Class name of std::string is "NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"
Class name of int is "i"
Class name of float is "f"
Class name of char is "c"