这是个有点奇怪的问题。我的目标是理解语言设计决策,并确定在c++中反射的可能性。

为什么c++语言委员会没有在语言中实现反射?在不运行在虚拟机上的语言(如java)中反射是否太困难了? 如果要在c++中实现反射,会遇到什么挑战?

我想反射的用途是众所周知的:编辑器可以更容易地编写,程序代码将更小,可以为单元测试生成模拟等等。但是如果你能评论一下反射的用法就太好了。


当前回答

The reason C++ doesn't have reflection is that this would require the compilers to add symbol information to the object files, like what members a class type has, information about the members, about the functions and everything. This essentially would render include files useless, as information shipped by declarations would then be read from those object files (modules then). In C++, a type definition can occur multiple times in a program by including the respective headers (provided that all those definitions are the same), so it would have to be decided where to put the information about that type, just as to name one complication here. The aggressive optimization done by a C++ compiler, which can optimize out dozens of class template instantiations, is another strong point. It's possible, but as C++ is compatible to C, this would become an awkward combination.

其他回答

The reason C++ doesn't have reflection is that this would require the compilers to add symbol information to the object files, like what members a class type has, information about the members, about the functions and everything. This essentially would render include files useless, as information shipped by declarations would then be read from those object files (modules then). In C++, a type definition can occur multiple times in a program by including the respective headers (provided that all those definitions are the same), so it would have to be decided where to put the information about that type, just as to name one complication here. The aggressive optimization done by a C++ compiler, which can optimize out dozens of class template instantiations, is another strong point. It's possible, but as C++ is compatible to C, this would become an awkward combination.

在c++中使用反射的情况有很多,而使用模板元编程等编译时结构无法充分解决这些问题。

N3340建议用富指针作为c++中引入反射的一种方式。除此之外,它还解决了一个问题,那就是除非你使用某个功能,否则就不用为它付费。

在过去的10年里,人们一直在尝试向c++中添加反射。最新的提案是针对c++23的,可能会,也可能不会。

与大多数语言中的反射不同,c++反射的计划是编译时反射。所以在编译时,你可以反射结构成员、函数和方法参数和属性、枚举值和名称等。

然后,您可以进行有限的具体化,注入关于反射的信息以生成其他类型和代码。

虽然这有点奇怪,但这意味着不使用反射的程序不会为它支付运行时成本。它也非常强大。

最简单的例子是,您可以使用它来实现运行时反射。

struct Member {
  std::string_view name;
  std::any_ref value;
};

struct Reflectable {
  virtual std::span<Member> GetMembers() const = 0;
  virtual std::span<Member> GetMembers() = 0;
};

template<class D>
struct ImplReflectable:Reflectable {
  std::span<Member> GetMembers() const final;
  std::span<Member> GetMembers() final;
};
template<class D>
std::span<Member> ImplReflectable<D>::GetMembers() const {
  // compile time reflection code on D here
}
template<class D>
std::span<Member> ImplReflectable<D>::GetMembers() {
  // compile time reflection code on D here
}

你把上面的代码写了一次,突然你就可以对任何你想要反射的类型,你可以这样做:

struct Point : ImplReflectable<Point> {
  int x, y;
};

和一个反射系统连接到点。

实现此运行时反射的库可以像您喜欢的那样复杂和强大。每种类型都必须做一些工作(如上所述)才能选择加入,但对于UI库(例如)这样做并不是一个严重的问题。没有选择的类型延续了c++的假设:“如果你不使用它,就不要为它付费”。

但这仅仅是个开始。一个提议,元类,允许:

interface Reflectable {
  std::span<Member> GetMembers() const;
  std::span<Member> GetMembers();
};

您可以使用元类或接受类型并返回类型的函数。这允许您定义类的元类,如“interface”,用语言编写。现在,接口有点像玩具,但是你可以编写QObject或Reflectable或PolymorphicValueType或NetworkProtocol元类来修改你的类定义的含义。

这可能会也可能不会出现在c++23中。它会继续变得更好,但也会继续被推回去。对于大多数主要的c++编译器,您可以尝试多种编译时反射实现。语法是不断变化的,因为有基于符号运算符的反射库,基于reflexpr的运算符反射库,其中一些反射数据是类型,另一些是constexpr对象和consteval函数。

如果c++可以:

变量名、变量类型和const修饰符的类成员数据 函数参数迭代器(只有位置而不是名称) 函数名、返回类型和const修饰符的类成员数据 父类列表(与定义的顺序相同) 模板成员和父类的数据;扩展的模板(意味着实际的类型将可用于反射API,而不是“如何到达那里的模板信息”)

这足以在无类型数据处理的关键处创建非常容易使用的库,而无类型数据处理在当今的web和数据库应用程序中非常普遍 (所有的orm,消息传递机制,xml/json解析器,数据序列化等)。

例如,Q_PROPERTY宏(Qt框架的一部分)支持的基本信息 http://qt.nokia.com/doc/4.5/properties.html扩展到涵盖类方法和e) -将对c++和一般的软件社区非常有益。

当然,我所指的反射不会涵盖语义或更复杂的问题(如注释、源代码行号、数据流分析等)——但我也不认为这些是语言标准的一部分。

如果你真的想了解c++的设计决策,可以找一本Ellis和Stroustrup写的《c++注释参考手册》。它并不是最新的标准,但它贯穿了最初的标准,并解释了事情是如何工作的,以及它们是如何实现的。