以编程方式检索C++类名

59

我想知道在C++中是否有可能以字符串形式检索类的名称,而无需硬编码为变量或getter。 我知道这些信息都不会在运行时使用,因此不可用,但是否有任何宏可以创建此功能?

编辑:需要注意的是,我实际上正在尝试检索派生类的名称,并且我正在使用Visual C++ 2008 Express Edition。


由于这取决于编译器,你使用哪个编译器? - Johannes Schaub - litb
我正在使用Visual C++ 2008 Express,我想指出的是,我实际上正在尝试检索派生类的名称。 - Morgan
也许这个链接可以帮到你:https://dev59.com/23A65IYBdhLWcg3wxBer#3649351? - WARhead
5个回答

92
您可以使用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> >。 "美化" 类型别名并不是一件容易的事情。


1
有趣,我不知道这个。它似乎工作得相当好。它给了我比我想要的回复多一点的文本,但它看起来相当不错。谢谢! - Morgan
如果类中没有虚方法,这个会起作用吗?我认为在这种情况下RTTI不起作用。我想只要有一个虚析构函数,你就没问题了。 - LeopardSkinPillBoxHat
7
@LeopardSkinPillBoxHat:是的,它会起作用(见§5.2.8/3和4)。普遍误解是typeid仅适用于多态类型,这可能源于与RTTI功能类似。 - 事实上,在静态类型上使用typeid不需要也不使用RTTI。该运算符在编译时计算,并将结果编译进去(严格来说,这是一项实现细节,但这是唯一合理的实现)。 - Konrad Rudolph

35

如果你只是想检查它是否属于某个类,那么

typeid(obj) == typeid(CSubClass)

无论如何都会起作用,不受实现方式的影响。

否则,一种方便的方法是声明:

virtual const char* classname() { return "CMyClass";}

并在每个子类中实现。


8
简单易懂易编译。很遗憾,这是错误的。typeid()返回一个指针typeinfo*。如果两个typeinfo*指针相等,则它们引用相同的类型,但如果它们不相等,则它们仍然可能引用相同的类型。这就是为什么有一个带有适当语义的std::type_index类。只有当两种类型相等时,std::type_index(typeid(obj)) == std::type_index(typeid(CSubClass)) 才会为真。 - MSalters

11

typeid(obj).name() 会始终返回变量被声明时的类型,而不是实际对象所属的类。如果变量 obj 被赋值为其父类的子类的实例,则 typeid 不会揭示这一点。


7
使用GCC 4.7.3版本,使用typeid(*somePtr).name()可以获得具体类的名称。 - notlesh
4
我遇到了相同的问题,但是@stephelton的评论让我意识到我是在指针上调用它而不是实际的对象或引用,导致返回指针的类型!只需添加 * 就可以解决问题。 - Mark Ransom
确认上述两个关于VS的评论。在执行BaseClass* ptr = new SubClass;之后,我发现typeid(ptr).name()返回class BaseClass *,而typeid(*ptr).name()则返回class SubClass - Jonathan Lidbeck
@JonathanLidbeck在去掉"Class"前缀后,找到了正确的类名吗? - Pabitra Dash

1

使用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。请注意,它不是可移植的;例如,英特尔的编译器不受支持。


1

关于这个问题,

在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"

在Ubuntu 18.04中,
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"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接