【问题标题】:C++0x, Compiler hooks and hard coded languages featuresC++0x、编译器钩子和硬编码语言特性
【发布时间】:2010-12-23 20:52:56
【问题描述】:

我对 C++0x 的一些新特性有点好奇。特别是range-based for loopsinitializer lists。这两个功能都需要用户定义的类才能正常运行。

我遇到了this post,虽然最佳答案很有帮助。我不知道这是否完全正确(我可能只是完全误解了,请参阅第一个答案的第三条评论)。根据初始化列表的current specifications,标头定义了一种类型:

template<class E> class initializer_list {
public:
    initializer_list();

    size_t size() const; // number of elements
    const E* begin() const; // first element
    const E* end() const; // one past the last element
};

你可以在规范中看到这个,就是 Ctrl + F 'class initializer_list'

为了将= {1,2,3} 隐式转换为initializer_list 类,编译器必须了解{}initializer_list 之间的关系。没有构造函数可以接收任何内容,所以据我所知,initializer_list 是一个包装器,它绑定到编译器实际生成的任何内容。

这与for( : ) 循环相同,它也需要用户定义的类型才能工作(尽管根据规范,更新为不需要任何数组和初始化列表的代码。但初始化列表需要&lt;initializer_list&gt;,所以这是代理用户定义的代码要求)。

我完全误解了这里的工作原理吗?我认为这些新功能实际上非常依赖用户代码并没有错。感觉好像功能是半生不熟的,而不是将整个功能构建到编译器中,而是由编译器完成一半,在包含中完成一半。这是什么原因?

编辑:我输入了“严重依赖编译器代码”,而不是“严重依赖用户代码”。我认为这完全摆脱了我的问题。我的困惑不在于编译器中内置了新功能,而是编译器中内置的东西依赖于用户代码。

【问题讨论】:

    标签: c++ c++11 compiler-construction initializer-list


    【解决方案1】:

    我认为这些新功能实际上非常依赖编译器代码并没有错

    它们确实非常依赖编译器。无论您是否需要包含标头,事实是在这两种情况下,今天的编译器都会出现语法解析错误。 for (:) 不太符合当今的标准,其中唯一允许的构造是 for(;;)

    感觉好像功能是半生不熟的,而不是将整个功能构建到编译器中,而是由编译器完成一半,在包含中完成一半。这是什么原因?

    该支持必须在编译器中实现,但您需要包含系统头文件才能使其工作。这可以服务于几个目的,在初始化列表的情况下,它将类型(编译器支持的接口)带入用户的范围,以便您可以使用它(想想 va_args 在 C 中的方式)。对于基于范围的 for (这只是语法糖),您需要将 Range 带入范围,以便编译器可以执行它的魔法。请注意,标准将for ( for-range-declaration : expression ) statement 定义为等同于(草案中的[6.5.4]/1):

    { 
       auto && __range = ( expression ); 
       for ( auto __begin = std::Range<_RangeT>::begin(__range), 
             __end = std::Range<_RangeT>::end(__range); 
             __begin != __end; 
             ++__begin ) { 
          for-range-declaration = *__begin; 
          statement 
       } 
    } 
    

    如果您只想在没有Range 概念的情况下(不是 C++0x 意义上的)可以实现的数组和 STL 容器上使用它,但是如果您想将语法扩展到用户定义的类(您的自己的容器)编译器可以很容易地依赖于现有的Range 模板(具有您自己的可能的专业化)。依赖于被定义模板的机制相当于要求容器上有一个静态接口。

    大多数其他语言已经朝着需要常规接口(比如容器,...)并在其上使用运行时多态性的方向发展。如果这要在 C++ 中完成,整个 STL 将不得不经历一次重大的重构,因为 STL 容器不共享公共基础或接口,并且它们不准备用于多态。

    如果有的话,当前的标准在其淘汰时不会不成熟

    【讨论】:

    • 我可以理解 for( : ) 构造的要求,如果没有编译器需要不同的容器迭代是不可能的。但是 for( : ) 不需要类型才能工作,只有当您打算扩展它时。这对我来说感觉类似于放置新的重载机制。但是,我仍然看不到 initializer_type 列表的原因。 = { 1,2,3,4,5 } 到数组文字,传递给 void 运算符{}( int arr[], 5 );看起来更干净,并且不需要包含系统。如果用户想将其放入容器中,他们可以在该函数中完成。
    • 正如您提到的 va_args,函数和宏是围绕编译器功能构建的。不包含 va_args 不会出现编译器错误,您可以自己编写。类似地,C++0x [](){} lambdas 不需要 std::function,您可以再次编写自己的。另一方面,{} 要求存在特定的类名,如果不存在,则会崩溃。而且不应该被强迫,我想我只是 nerd raging 但我在 C++0x 标准中看到的很多东西似乎越来越陌生。
    • 比这要复杂一些。编译器和 STL 实现者不需要相同。 Microsoft 使用 Dinkunware STL 实现,但您可以使用 STLPort 或任何其他实现。 STL 类不是 100% 定义和确定的,STL 实现者可以决定添加新模板参数,只要它们具有默认值,因此可以在仅依赖和使用标准中的那些的用户代码中使用。这意味着编译器本身并不能真正轻松地匹配 STL 容器。
    • 它是 STL 的实现者,它提供了编译器可能需要的额外信息,此时它还允许您在容器上提供额外的“带外”信息。 good-old-stl 已经在算法中使用了特征,这是一个常见的习惯用法:在外部模板中提供额外的信息。现行标准只是沿用了原标准的趋势。为什么您希望将这些功能从它们所在的位置移除,如果 Range 存在,您为什么不希望实现者将其用于 STL?
    • 我发现奇怪的不是使用编译器提供的东西的 STL,例如,特征和可变参数省略号是 STL 构建的东西。我发现奇怪的是,与依赖编译器功能的 STL 不同,编译器现在依赖于 STL 功能。目前我所知道的没有编译器功能实际上依赖于代码的存在。至少不是用户必须手动提供的代码,for(:) 循环是我能理解的一个例外,但与 {} 不同,它不会强制存在代码。
    【解决方案2】:

    这只是语法糖。编译器会将给定的语法结构扩展为直接引用标准类型/符号名称的等效 C++ 表达式。

    这不是现代 C++ 编译器在其语言和“外部世界”之间唯一的强耦合。例如,extern "C" 是一种语言 hack,以适应 C 的链接模型。声明线程本地存储的面向语言的方式隐式依赖于大量 RTL 黑客技术。

    或者看看C。你如何访问通过...传递的参数?你需要依赖标准库;但它使用的魔法非常依赖于 C 编译器如何准确地布置堆栈帧。

    更新:

    如果有的话,C++ 在这里采取的方法更符合 C++ 的精神,而不是替代方案 - 它是添加一个 intrinsic 集合或范围类型,嵌入到语言中。相反,它是通过供应商定义的范围类型完成的。我真的不认为它与可变参数参数有什么不同,如果没有供应商定义的访问器宏,它们同样无用。

    【讨论】:

    • ... 和 extern "C" 虽然是语言特性,但您不需要编写将 ... 扩展为参数的类,您只需使用 ... 来指示编译器应该做什么做。 {} 另一方面,需要一个名为 initializer_list 的类,如果该类不存在,它将无法工作,您可以编译不包含任何内容的函数 (int a,...)。
    • 戴夫,如果没有 RTL 中的胆量,thread_local 将无法工作,异常分派支持也不会。我认为您刚刚过着隐蔽的生活 :) - 编译器在 RTL 的合作下所做的工作比您意识到的要多,而您只是认为这是理所当然的。现在出现了另一个需要 RTL 支持 (std::initializer_list) 的功能 ({}),你会大吃一惊!
    • 当然不是,但是 thread_local 存储标识符就是这样,一个指定要做什么的标识符。我对编译器做后台工作没有任何问题。你的例子......例如,......语言功能不需要 va_list 工作。 va_list 是围绕编译器的一个特性构建的,你可以自己写。另一方面, std::initializer 需要存在代码才能工作,您可以编译没有包含的函数( int, ... )。当然,{} 如何需要 RTL 支持?
    • 戴夫,还有什么选择?它需要有一个类型。那会是什么类型?它要么是 C++ 固有的(当然它仍然有一个隐藏的 RTL 实现,什么都不是什么),或者它会在标准头文件中定义。
    • 突然想到一个运算符函数:void operator{}(int array[]);对我来说 some_class= { 1,2,3,4 } 比 void function(initializer_list ) 更有意义
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-24
    • 2023-04-09
    • 2014-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-05
    相关资源
    最近更新 更多