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

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

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


当前回答

Reflection requires some metadata about types to be stored somewhere that can be queried. Since C++ compiles to native machine code and undergoes heavy changes due to optimization, high level view of the application is pretty much lost in the process of compilation, consequently, it won't be possible to query them at run time. Java and .NET use a very high level representation in the binary code for virtual machines making this level of reflection possible. In some C++ implementations, however, there is something called Run Time Type Information (RTTI) which can be considered a stripped down version of reflection.

其他回答

在过去的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++要用作数据库访问、Web会话处理/http和GUI开发的语言,那么c++中的反射是至关重要的。缺乏反射阻碍了orm(如Hibernate或LINQ)、实例化类的XML和JSON解析器、数据序列化和许多其他东西(最初必须使用无类型数据来创建类的实例)。

可以使用软件开发人员在构建过程中可用的编译时开关 为了消除这种“一分钱一分货”的顾虑。

我是一个固件开发人员,不需要反射来从串口读取数据——那么很好,不使用交换机。但是作为一个想要继续使用c++的数据库开发人员,我经常要面对一个可怕的、难以维护的代码,这些代码在数据成员和数据库结构之间映射数据。

无论是Boost序列化还是其他机制都不能真正解决反射问题——它必须由编译器来完成——一旦完成,c++将再次在学校中教授,并用于处理数据处理的软件中

对我来说,这是问题#1(而原生线程原语是问题#2)。

根据Alistair Cockburn的说法,在反射环境中不能保证子类型。

反射与潜在类型系统更相关。在c++中,你知道你得到了什么类型,你知道你可以用它做什么。

Reflection requires some metadata about types to be stored somewhere that can be queried. Since C++ compiles to native machine code and undergoes heavy changes due to optimization, high level view of the application is pretty much lost in the process of compilation, consequently, it won't be possible to query them at run time. Java and .NET use a very high level representation in the binary code for virtual machines making this level of reflection possible. In some C++ implementations, however, there is something called Run Time Type Information (RTTI) which can be considered a stripped down version of reflection.

反射可以在c++中实现,并且已经在c++中实现过。

它不是原生的c++特性,因为它有一个沉重的成本(内存和速度),不应该由语言默认设置——语言是“默认最大性能”导向的。

因为你不应该为你不需要的东西付钱,而且正如你自己所说,编辑器比其他应用程序更需要它,那么它应该只在你需要的地方实现,而不是“强制”到所有的代码中(你不需要对你将在编辑器或其他类似应用程序中使用的所有数据进行反思)。