为什么C ++没有反射?

Why does C++ not have reflection?

这是一个有点奇怪的问题。我的目标是理解语言设计决策,并识别C++中的反射可能性。

  • 为什么C++语言委员会没有在语言中实施反思?在不在虚拟机上运行的语言(如Java)中,反射是否太难了?

  • 如果要实现C++的反思,将面临哪些挑战?

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


    C++中存在反射问题。好的。

    • 还有很多工作要做,C++委员会是相当保守的,不要花时间在激进的新特征上,除非他们确信它会有回报。(提出了一个添加类似于.NET程序集的模块系统的建议,虽然我认为有一个普遍的共识,那就是它很好,但这不是目前他们的头等大事,并且在C++0X之后被推回到很好。这个特性的动机是去掉EDCOX1×0系统,但它也能使AT至少一些元数据)。好的。

    • 你不为你没有的付出代价使用。这是必须的基本条件之一C++基础上的设计哲学。为什么我的代码要随身携带如果我永远不需要元数据?此外,元数据的添加可能会禁止编译器优化。我为什么要付那个如果我不需要的话,代码中的成本元数据?好的。

    • 这又引出了另一个大问题:C++很少保证关于编译的代码。这个编译器可以做漂亮的喜欢什么都行,只要产生的功能是是意料之中的事。例如,您的实际上并不要求课程在那里。编译器可以直接优化它们。他们所做的一切经常这样做,因为即使是简单的模板代码也会创建相当多的模板实例化。C++标准图书馆依赖这种侵略性优化。函数只是如果架空实例化和销毁对象可以被优化。矢量上的operator[]仅可与raw比较。数组索引性能因为整个运算符可以内部,因此完全移除从编译的代码。C语言与Java语言对编译器的输出。如果我定义C中的一个类,那么该类将存在于生成的程序集中。即使我从不使用它。即使所有调用其成员函数可以内联。这门课必须是在那里,这样反射就可以找到它。其中一部分被C所缓解。#编译到字节码,这意味着JIT编译器可以删除的类定义和内联功能,如果它喜欢,即使初始C编译器不能,在C++中,你只有一个编译器,它必须输出有效的代码。如果你允许检查元数据对于C++可执行文件,您可以预期查看它定义的每个类,其中意味着编译器将为了保留所有定义的类,即使它们不是必需的。好的。

    • 还有模板。C++中的模板什么都不是其他语言的通称。每个模板实例化创建一个新型。std::vector是一个完全独立于std::vector。这等于整体上有很多不同的类型程序。我们的思考应该是什么看到了吗?模板std::vector?但是怎么可能呢,因为那是源代码构造,它没有运行时的意义?一定要看看不同的班级std::vectorstd::vector。和std::vector::iteratorstd::vector::iterator,同对于const_iterator等。和一旦进入模板元编程,你很快就会结束实例化数百个模板,所有这些都被内联并删除再次由编译器执行。他们没有含义,除了作为编译时元程序。都应该这几百个类是可见的反思?他们必须这样做,因为否则我们的反思如果它甚至不能保证我定义的类实际上就在那里,那将是无用的。另一个问题是模板类在被实例化之前是不存在的。设想一个使用std::vector的程序。我们的反射系统能看到std::vector::iterator吗?一方面,你肯定会希望如此。它是一个重要的类,它是用std::vector定义的,元数据中确实存在。另一方面,如果程序从未实际使用这个迭代器类模板,那么它的类型将永远不会被实例化,因此编译器不会首先生成类。在运行时创建它还为时已晚,因为它需要访问源代码。好的。

    • 最后,反思并不完全在C++中是至关重要的,因为它是C语言中的。这个原因再次出现,模板元编程。它解决不了一切,但在很多情况下否则你会求助于思考,可以写一个执行相同操作的元程序编译时的事件。boost::type_traits是一个简单的例子。你想知道类型T号?检查其type_traits。在C,之后你得到处钓鱼使用反射键入。反射对一些人还是有用的事物(我能看到的主要用途,哪些元编程不容易替换,用于自动生成序列化代码),但它会为C++,它不像其他语言那样经常需要。

    编辑:回应评论:好的。

    CDLAREY:是的,调试符号做了类似的事情,因为它们存储关于可执行文件中使用的类型的元数据。但他们也有我描述的问题。如果你曾经试过调试一个发布版本,你就会明白我的意思了。在源代码中创建类时存在很大的逻辑间隙,而这些类在最终代码中是内联的。如果要对任何有用的东西使用反射,就需要它更加可靠和一致。实际上,几乎每次编译时类型都会消失。您更改了一个小细节,编译器决定更改哪些类型是内联的,哪些不内联的,作为响应。当您甚至不能保证在元数据中表示最相关的类型时,如何从中提取有用的内容?您要查找的类型可能在上一个版本中就存在,但现在已经不存在了。明天,有人将签入一个小的无辜的改变到一个小的无辜的功能,这使得类型只是足够大,它不会得到完全内联,所以它将再次回来。这对于调试符号仍然有用,但不比这多。我不想在这些条件下为类生成序列化代码。好的。

    埃文·泰兰:当然,这些问题可以解决。但这又回到了我的观点。这需要大量的工作,C++委员会有很多他们认为更重要的事情。在C++中得到一些有限的反射(这将是有限的)的好处是否足够大,足以以牺牲其他特性为代价来强调这一点?添加核心语言功能真的有很大的好处吗?这些功能已经(大部分)通过库和像qt这样的预处理器完成了?也许吧,但这种需求比不存在这样的图书馆要急迫得多。不过,对于您的具体建议,我认为在模板上不允许它会使它完全无用。例如,您将无法使用标准库上的反射。什么样的反射不会让你看到一个std::vector?模板是C++的一个重要组成部分。不适用于模板的功能基本上是无用的。好的。

    但你是对的,可以实现某种形式的反射。但这将是语言的重大变化。现在,类型只是一个编译时构造。它们的存在是为了编译器的利益,而不是为了别的。一旦代码被编译,就没有类了。如果您扩展自己,您可能会认为函数仍然存在,但实际上,所有的都是一堆跳跃汇编程序指令,以及大量的堆栈推送/弹出指令。在添加此类元数据时,没有什么可继续的。好的。

    但正如我所说,有一个对编译模型进行修改的建议,添加独立的模块,存储选定类型的元数据,允许其他模块引用它们,而不必与#include混淆。这是一个好的开始,老实说,我很惊讶标准委员会并没有因为o大变化。也许5-10年后?:)好的。好啊。


    反射需要一些关于类型的元数据存储在可以查询的地方。由于C++编译为本机代码,由于优化而经历了很大的变化,所以应用程序的高层次视图在编译过程中几乎丢失了,因此在运行时无法查询它们。Java和.NET在虚拟机的二进制代码中使用了非常高级别的表示,使这种反射成为可能。然而,在一些C++实现中,有一种称为运行时类型信息(RTTI)的东西,它可以被认为是反射的精简版本。


    在should not every languages of every尽量纳入其他语言特征。P></

    C + +是很本质,甚sophisticated宏汇编。EN is not(在传统意识中高级语言)#类C、Objective-C、Java、Smalltalk等。P></

    它的好是to have different Tools for different乔布斯。if we have only在西汉姆,事情会看起来像你的脚本语言等。有is useful for some乔布斯,和反射的面向对象的语言(Java、C—C对象,# useful for another of class)是乔布斯,和超高效的棺材骨头近于-人机语言are useful for yet another class of乔布斯(C、C++、汇编)。P></

    C++ does安伸科技奇异job of levels of汇编到不可思议的复杂性和管理,让更多的空气日期2010年1月17 abstractions编程任务,更多的人vastly possible for复合我。but that is not a en is the best necessarily语言suited for那些谁是他们的问题approaching从高层次的角度严格(LISP,Smalltalk,Java,C #)。如果你需要在这些语言特征与最佳的解决方案在实施"订单",那么你的问题,谢谢你的那些谁是在美国这样for of languages created to使用!P></

    但那些谁is for C++,无论reason for(S),need to have a Strong and the between their尾关机操作的基础。whether or programming device司机其效率,或互动,与下级OS服务,任何C + +或更好,suited is to那些任务。P></

    # C,Java,Objective-C在空气日期2010年1月17 richer要求多,运行时系统执行的支持他们。that has to be to the交付运行支持系统的问题- the operation of your预装软件。那层has to be for各种目标系统的维护,customized by some other language,让工件的在线平台。中间那层,主机操作系统和自适应层between the the is the运行你的代码,几乎总是在类C语言或书面在C + +在#效率是1,在predictably精确理解软件和硬件交互的between the can be to understood阱,和manipulated最大增益。P></

    爱和Smalltalk,Objective-C运行时系统,有丰富的元数据,和反射,垃圾收集等。奇异can be written to take Code of these设施优势!但这只是在更高的层,在层上的堆栈,网络层必须休息下,我必须坐在themselves ultimately the OS和硬件。我们将永远是最好的suited that need a language for that层:建筑/ C / C + +程序。P></

    增编:C + + / 14 11是继续扩大到C + +的支持能力abstractions和高阶系统。线程,同步的准确记忆,抽象机模型,使更多的精确定义是C + +是developers to many of the abstractions高层次,高层次的看待这些"used to have only languages域而过,继续提供近于金属性能和优异的运行(1 predictability极小subsystems)时间。也许我会在selectively反射设施促成未来revision of for C++,那些谁想要它-或也许会提供这样的服务在运行时库(there is one or…现在,有人在一个升压of the?)。P></


    如果您真的想了解C++的设计决策,请找到埃利斯和StruouStrup注释的C++参考手册的副本。它不是最新标准的最新版本,但是它通过了原始标准,解释了事情是如何工作的,以及它们是如何实现的。


    对于具有反射功能的语言,反射功能是关于编译器愿意在对象代码中保留多少源代码来启用反射功能,以及有多少分析机制可以用来解释反射的信息。除非编译器保留了所有源代码,否则反射在分析源代码可用事实方面的能力将受到限制。

    C++编译器不保留任何东西(好,忽略RTTI),所以你不会在语言中得到反射。(Java和C语言编译器只保留类、方法名和返回类型,所以你得到一点反射数据,但是你不能检查表达式或程序结构,这意味着即使在那些"反射启用"的语言中,你能得到的信息非常稀疏,因此你真的不能做太多的分析)。

    但是,您可以跳出语言,获得完整的反射能力。另一个关于C中反射的堆栈溢出讨论的答案讨论了这一点。


    反射可以在C++中实现,也可以在C++中实现。

    它不是本机C++特性,因为它有一个沉重的成本(内存和速度),不应该被默认设置的语言-语言是"最大性能默认"为导向。

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


    C++没有反射的原因是,这会要求编译器将符号信息添加到对象文件中,比如类成员拥有什么、成员的信息、函数和一切。这基本上会使include文件变得无用,因为声明所传递的信息随后将从这些对象文件(然后是模块)中读取。在C++中,一个类型定义可以在程序中多次出现,包括各自的头(如果所有的定义都是相同的),那么就必须决定把那个类型的信息放在哪里,就像在这里列出一个复杂的问题一样。C++编译器所做的积极优化,可以优化几十个类模板实例化,是另一个优点。这是可能的,但是当C++与C兼容时,这将成为一个尴尬的组合。


    there are of cases for using吨反射cannot be that C++的编译时间constructs adequately addressed类使用模板元编程。P></

    n3340分枝方式分为丰富的介绍了C++中的反射。other things要求EN addresses of not for the问题,除非你支付的特征在使用它。P></


    反射是一个可选的,预处理器指令。something likeP></

    #pragma enable reflectionP></

    that have the best of方式我们可以与这世界都会出局,杂注好图书馆(without any created没有反射overheads as discussed),然后它会弹出developer whether they be the个人使用或想轻松和速度。P></


    根据AlistairCockburn的说法,在反射环境中无法保证子类型。

    反射与潜在的打字系统更相关。在C++中,你知道你得到了什么类型,你知道你能用它做什么。


    如果C++可以:

    • 变量名、变量类型和const修饰符的类成员数据
    • 函数参数迭代器(仅位置而不是名称)
    • 函数名、返回类型和const修饰符的类成员数据
    • 父类列表(按定义的顺序)
    • 模板成员和父类的数据;扩展的模板(意味着反射API可以使用实际类型,而不是"如何到达那里的模板信息")。

    这足以创建非常易于使用的库,而库正是当今Web和数据库应用程序中普遍存在的无类型数据处理的关键所在。(所有的窗体、消息传递机制、XML/JSON解析器、数据序列化等)。

    例如,Q_PROPERTY宏(qt框架的一部分)支持的基本信息HTTP://QT.NOKIACOM/DOC/4.5/Actusi.HTML扩展到覆盖类方法和E)对C++和一般的软件社区都是非常有益的。

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


    一些关于C++中反射的好链接我刚刚发现:

    C++标准工作文件:C++中的反射问题

    使用模板反射的简单示例


    这基本上是因为它是一个"可选的额外的"。许多人选择C++,而不是像Java和C语言这样的语言,这样他们就可以对编译器输出进行更多的控制,例如更小的程序和/或更快的程序。

    如果选择添加反射,则可以使用各种解决方案。


    在C++中,如果C++被用来作为数据库访问、Web会话处理、HTTP和GUI开发的语言,我认为这一点至关重要。缺少反射会阻止ORM(如Hibernate或Linq)、XML和JSON解析器来实例化类、数据序列化和许多其他thign(最初必须使用无类型数据来创建类的实例)。

    可以使用软件开发人员在构建过程中可用的编译时开关。为了消除这种"你为你所用的东西买单"的顾虑。

    i firmwaredeveloper不需要反射来从串行端口读取数据——那么很好,不要使用开关。但是,作为一个想要继续使用C++的数据库开发人员,我经常会遇到一个可怕的、难以维护的代码,它将数据映射到数据成员和数据库结构之间。

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

    对我来说,这个问题1(而native threading原语是问题2)。