为什么 std::initializer_list 不是内置语言?
Why isn't std::initializer_list a language built-in?
为什么std::initializer_list
不是内置的核心语言?
在我看来,这是c++ 11的一个相当重要的特性,但它没有自己的保留关键字(或类似的东西)。
相反,initializer_list
it's 只是一个来自标准库的模板类,它有一个特殊的,隐式的映射从新的括号-初始化列表 {...}
语法,由编译器处理。
乍一看,这个解决方案相当hacky。
这是c++语言的新添加的实现方式吗:通过隐式角色一些模板类,而不是通过核心语言?
请考虑以下例子:
widget<int> w = {1,2,3}; //this is how we want to use a class
为什么选择一个新类:
widget( std::initializer_list<T> init )
而不是使用与这些想法类似的
widget( T[] init, int length ) // (1)
widget( T... init ) // (2)
widget( std::vector<T> init ) // (3)
- 一个经典的数组,你可以在这里和那里添加
const
- 三个点在语言中已经存在(var-args,现在是可变模板),为什么不重用语法(并使其感觉内置)
- 只是一个现有的容器,可以添加
const
和&
它们都已经是语言的一部分。我只写了我的第一个想法,我相信还有很多其他方法。
已经有返回在std
命名空间中定义的类型的"核心"语言特性的例子。typeid
返回std::type_info
, sizeof
返回std::size_t
。
在前一种情况下,为了使用所谓的"核心语言"特性,您已经需要包含一个标准头文件。
现在,对于初始化列表,生成对象时不需要关键字,语法是上下文敏感的花括号。除此之外,它与type_info
相同。就我个人而言,我不认为缺少关键字会使它"更hacky"。这可能更令人惊讶,但请记住,目标是允许使用与聚合相同的括号初始化器语法。
所以,是的,你可能会期待在未来更多的设计原则:
- 如果更多的情况出现,在没有新关键词的情况下可以引入新功能,那么委员会将采用它们。
- 如果新功能需要复杂的类型,那么这些类型将被放在
std
中,而不是作为内置的。
:
- 如果一个新特性需要一个复杂的类型,并且可以在没有新的关键字的情况下引入,那么你就会得到你在这里所拥有的,这是"核心语言"语法,没有新的关键字,并使用
std
的库类型。
归根结底,我认为,在c++中,"核心语言"和标准库之间没有绝对的区别。它们在标准中是不同的章节,但每个章节都引用了另一个章节,而且一直都是这样。
在c++ 11中还有另一种方法,那就是lambda引入具有由编译器生成的匿名类型的对象。因为它们没有名称,所以它们根本不在名称空间中,当然不在std
中。但是,对于初始化列表来说,这不是一种合适的方法,因为在编写接受类型名的构造函数时使用了类型名。
c++标准委员会似乎更倾向于不添加新的关键字,可能是因为这会增加破坏现有代码的风险(遗留代码可以使用该关键字作为变量,类或其他任何东西的名称)。
此外,在我看来,将std::initializer_list
定义为模板化容器是一个相当优雅的选择:如果它是一个关键字,您将如何访问其底层类型?你将如何遍历它?您还需要一堆新的操作符,这将迫使您记住更多的名称和更多的关键字来完成与标准容器相同的操作。
将std::initializer_list
视为任何其他容器,使您有机会编写与任何这些东西一起工作的泛型代码。
更新:
那么为什么要引入一个新的类型,而不是使用现有的一些组合?(来自评论)
首先,所有其他容器都有添加、删除和放置元素的方法,这对于编译器生成的集合来说是不可取的。唯一的例外是std::array<>
,它包装了一个固定大小的c风格数组,因此仍然是唯一合理的候选。
然而,正如Nicol Bolas在评论中正确指出的那样,std::initializer_list
与所有其他标准容器(包括std::array<>
)的另一个根本区别在于后者具有值语义,而std::initializer_list
具有引用语义。例如,复制std::initializer_list
不会复制它所包含的元素。
此外(再次由Nicol Bolas提供),为大括号初始化列表提供一个特殊的容器允许在用户执行初始化的方式上重载。
这不是什么新鲜事。例如,for (i : some_container)
依赖于some_container
类中存在特定的方法或独立的函数。c#甚至更多地依赖于它的。net库。实际上,我认为这是一个相当优雅的解决方案,因为您可以使您的类与某些语言结构兼容,而无需使语言规范复杂化。
这确实不是什么新鲜事,有多少人指出,这种做法在c++中有,在c#中也有。
Andrei Alexandrescu提到了一个很好的观点:您可以将其视为虚构的"核心"名称空间的一部分,然后它会更有意义。
所以,它实际上是这样的:core::initializer_list
, core::size_t
, core::begin()
, core::end()
等等。这只是一个不幸的巧合,std
名称空间内部有一些核心语言结构。
不仅可以在标准库中完全工作。包含到标准库中并不意味着编译器不能玩聪明的把戏。
虽然它可能不能在所有情况下,它可以很好地说:这个类型是众所周知的,或者一个简单的类型,让我们忽略initializer_list
,只是有一个内存映像的初始值应该是什么。
换句话说,int i {5};
可以等同于int i(5);
或int i=5;
甚至intwrapper iw {5};
,其中intwrapper
是一个简单的int包装类,带有一个简单的构造函数,接受initializer_list
它不是核心语言的一部分,因为它可以完全在库中实现,仅行operator new
和operator delete
。让编译器更复杂来构建它会有什么好处?
- 内置函数可查看CPP中的成员变量
- 是否有内置方法可以强制转换为不同的基础类型,但保留常量限定符?
- 按字母顺序对字符串中的字母进行排序,而无需使用内置的 sort()
- 将字符串转换为浮点数或整数,而无需使用内置函数(如 atoi 或 atof)
- 如何从 c++ 中类中内置的数组继承
- 如何捕获 C++ 内置异常对象
- macOS 是内置在 clang 编译器中还是内置于 xcode ide 中?
- 将编译器开关添加到 Eclipse CDT 内置编译器设置生成?
- gcc Atomic在gcc 4.1.1中内置了奇怪的行为
- 是否有用于元素部分移位的 simd 指令/内在/内置指令?
- 何时包含内置类型和运算符的标头?
- 基本类型与内置类型有什么区别C++
- 指内置类型的文字
- GLUT 问题:重新声明 c++ 内置类型'wchar_t'时出错
- 像自定义类一样构造的指针(内置类型)如何工作?
- 内置类型与用户定义的类型 C++
- 将内置类型变量传递给只有一个类类型参数的"+"运算符函数时自动类型转换的构造函数
- 为什么像C++和Java这样的语言有一个内置的LinkedList数据结构
- matlab 内置函数是用某种较低级别的语言编写的吗?
- 为什么 std::initializer_list 不是内置语言?