【讨论】C++11的初始化列表类initializer_list是怎么跟{}关联起来的?

几罗星人 2017-01-21 12:26:02
上代码:

map<int, int> m;
m.insert({ {1,2},{3,4},{5,6},{7,8} });

这是在msdn的map类的insert函数的说明上看到的https://msdn.microsoft.com/zh-cn/library/81ac0zkz.aspx
页面里有示例代码:


{ {1,2},{3,4},{5,6},{7,8} }这种语法只在初始化二维数组的时候用过。上图的注释提到了初始化列表类initializer_list,于是写了上面的代码试一下,看了反汇编(二楼上反汇编分析),发现上述的代码与下面是等价的:

pair<const int, int> p1(1, 2);
pair<const int, int> p2(3, 4);
pair<const int, int> p3(5, 6);
pair<const int, int> p4(7, 8);
initializer_list<pair<const int, int>> il(&p1, &p4);
map<int, int> m;

m.insert(il);

也就是说{1,2}这样的对被编译为pair,最外层的大括号是initializer_list。那么{}是怎么跟initializer_list类联系起来的?为什么最外层的{}会被编译为initializer_list?

换句话说,我想自己实现一个功能与initializer_list一模一样的my_init_list行不行?
...全文
1217 24 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
赵4老师 2017-04-05
  • 打赏
  • 举报
回复
有那么些人喜欢或者适合用“先具体再抽象”的方法学习和理解复杂事物; 而另一些人喜欢或者适合用“先抽象再具体”的方法学习和理解复杂事物。 而我本人属前者。
FD_2013 2017-04-04
  • 打赏
  • 举报
回复
赵四 就是个典型的学院派,你跟他说问题,他跟你讲道理,你跟他讲道理,他跟你扯学术,对于这种学院派的人,我向来都瞧不上
赵4老师 2017-04-04
  • 打赏
  • 举报
回复
这个世界上发生过哪怕一次1+1=2吗?
几罗星人 2017-04-02
  • 打赏
  • 举报
回复
引用 18 楼 zhao4zhong1 的回复:
不要迷信书、考题、老师、回帖;
要迷信CPU、编译器、调试器、运行结果。
并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。
任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实!


赵四老师的水贴越来越严重了,注意一下啊,以前的赵四老师可不是这样的。

研究语言性质不研究编译器源代码?这个问题,我也不反对,不过性质归性质,与编译器没关系,就是标准里定好的东西;编译器也只是照着性质做而已,有些时候还做不出来。

据说就没有完全支持C++标准的C++编译器,g++的支持度虽然高,但是我也见到g++官方给的编译器违例报告,某一些标准的规定没有实现的。可能是因为编译原理的技术达不到,也有可能是在其他实现得不完善的情况下实现一个特性会引来一些严重的问题。

跑远了,或者问:能否运用C++标准现有的内容,实现自己设计的一个类与{}相关联,像initializer_list一样?
超级能量泡泡 2017-02-04
  • 打赏
  • 举报
回复
引用 10 楼 JiLuoXingRen 的回复:
实际上我一直在思考一个问题:当一种语言给出一种特性的时候,我们能不能用这个语言本身去实现它。 {}和initializer_list的关联是一个例子;还有学习java的时候,foreach的语法for(object o : target)可以遍历target,对于数组可以,对于内置的map也可以,那能否自己写一个类使之可以作为target呢?查了文档之后发现可以,实现Iterable接口就可以了。 我在想能否用语言本身实现给出的特性,可能是语言的一种性质。
直接重定义宏大括号和普通大括号冲突,不好搞 其实自己定义一个tiny_vector (on stack),类似C++11 array,然后用不定长参数模板函数 template<typename T, typename...Args> auto make_vector(Args...args) -> tiny_vector<T,sizeof...(args)> { // ... } 可以实现类似效果
赵4老师 2017-01-24
  • 打赏
  • 举报
回复
不要迷信书、考题、老师、回帖; 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实!
  • 打赏
  • 举报
回复
引用 15 楼 JiLuoXingRen 的回复:
[quote=引用 14 楼 zhao4zhong1 的回复:] 参考g++源代码相关片断。
赵老师,g++的代码你研究过吗?编译原理没看过,没这能力去看那代码。不过,我想研究的只是语言性质方面的内容,只要确定了是编译器特殊处理就行了,对于编译器具体如何实现怎不讨论。不过如果能看懂g++的代码,自然是能确定倒是是不是编译器做了特殊处理[/quote] 语言性质的话应该 看C++标准文档 ISO/IEC 14882:2014 根据情况 {}会匹配到 std::initializer_list<E>,自己写std::initializer_list也可以,关键是名字空间类名要一致。
赵4老师 2017-01-24
  • 打赏
  • 举报
回复
研究语言性质不研究编译器源代码?
赵4老师 2017-01-23
  • 打赏
  • 举报
回复
参考g++源代码相关片断。
几罗星人 2017-01-23
  • 打赏
  • 举报
回复
引用 12 楼 ri_aje 的回复:
你说的这个是 core language vs. library。c++ 的哲学是能用库实现的尽量不要通过扩展语言本身实现,比如 range for 只需要类型支持 begin/end 即可,但并非所有的特性都能这么处理,比如 lambda 就需要语言本身接支持。
确实是这样,如果能在核心语言上封装出来,这样的语言应该是比较……稳定的?不知道怎么描述。因为如果不断去拓展语言的话,弄不好就会变得乱七八糟,连编译器也要不断变来变去
ri_aje 2017-01-23
  • 打赏
  • 举报
回复
引用 10 楼 JiLuoXingRen 的回复:
实际上我一直在思考一个问题:当一种语言给出一种特性的时候,我们能不能用这个语言本身去实现它。 {}和initializer_list的关联是一个例子;还有学习java的时候,foreach的语法for(object o : target)可以遍历target,对于数组可以,对于内置的map也可以,那能否自己写一个类使之可以作为target呢?查了文档之后发现可以,实现Iterable接口就可以了。 我在想能否用语言本身实现给出的特性,可能是语言的一种性质。
你说的这个是 core language vs. library。c++ 的哲学是能用库实现的尽量不要通过扩展语言本身实现,比如 range for 只需要类型支持 begin/end 即可,但并非所有的特性都能这么处理,比如 lambda 就需要语言本身接支持。
ri_aje 2017-01-23
  • 打赏
  • 举报
回复
引用 9 楼 JiLuoXingRen 的回复:
[quote=引用 5 楼 ri_aje 的回复:] 编译器特殊处理是因为标准要求的,标准不管,谁给你特殊处理啊,别搞反了。 {...} 的语法叫 list initialization,标准要求先匹配 std::initializer_list 的重载,如果有的话。 你自己的私货进不了标准,当然编译器不屌了。 ps. 你学 c++ 的思路搞反了,正确的途径是学习标准,研究具体实现只能起辅助作用。本末倒置可能会学歪了,到时候功夫没少出,水平却不行。
谢谢,也就是说你也认同是编译器做了特殊处理,自己是不可能实现这种关联的?[/quote] list initialization 只认 std::initializer_list,标准目前是这么要求的,至于以后是否会改变,那就不知道了。
几罗星人 2017-01-23
  • 打赏
  • 举报
回复
引用 14 楼 zhao4zhong1 的回复:
参考g++源代码相关片断。
赵老师,g++的代码你研究过吗?编译原理没看过,没这能力去看那代码。不过,我想研究的只是语言性质方面的内容,只要确定了是编译器特殊处理就行了,对于编译器具体如何实现怎不讨论。不过如果能看懂g++的代码,自然是能确定倒是是不是编译器做了特殊处理
赵4老师 2017-01-22
  • 打赏
  • 举报
回复
“变量赋值”和“变量初始化”不是一回事! “变量赋值”发生在运行期,其写法遵循赋值语法规定。 “变量初始化”发生在编译期或运行期,其写法遵循初始化列表语法规定。
sdghchj 2017-01-22
  • 打赏
  • 举报
回复
不一定是数组,C语言里的结构体也可以用{}初始化
ri_aje 2017-01-22
  • 打赏
  • 举报
回复
引用 4 楼 JiLuoXingRen 的回复:
觉得是编译器做了特殊处理,才将{}编译为 [quote=引用 2 楼 ri_aje 的回复:] {} 的参数就会匹配吃 init list 的函数重载,如果有的话,没有的话,当 () 进行重载解析。 insert {{}} 的写法,外层的匹配 map insert 吃 init list 的重载,里面的每一个继续匹配 pair 吃 init list 的 ctor 重载,但是没有,所以调用的是双参数的 pair ctor. 你不用翻反汇编,标准要求的 init list 底层是数组支持。
那么按你说的,具有参数为initializer_list的重载就能将{}匹配到initializer_list? insert函数确实有一个重载是initializer_list,声明如下(参见https://msdn.microsoft.com/zh-cn/library/81ac0zkz.aspx):

// (6) initializer list
void insert(
    initializer_list<value_type> IList
);
但是,这样的语法不是{}能匹配到initializer_list的条件。我将<map>头文件的内容复制过来,将所有涉及类名initializer_list的都替换成my_init_list,结果表明{}并不能匹配到my_init_list 从复制<map>复制的代码:

#define _CONST_DATA	constexpr
#define _CONST_FUN	constexpr
#define _NOEXCEPT	noexcept

template<class _Elem>
class my_init_list		// 将类名改为my_init_list
{	// list of pointers to elements
public:
	typedef _Elem value_type;
	typedef const _Elem& reference;
	typedef const _Elem& const_reference;
	typedef size_t size_type;

	typedef const _Elem* iterator;
	typedef const _Elem* const_iterator;

	_CONST_FUN my_init_list() _NOEXCEPT // 将类名改为my_init_list
		: _First(0), _Last(0)
	{	// empty list
	}

	_CONST_FUN my_init_list(const _Elem *_First_arg, // 将类名改为my_init_list
		const _Elem *_Last_arg) _NOEXCEPT
		: _First(_First_arg), _Last(_Last_arg)
	{	// construct with pointers
	}

	_CONST_FUN const _Elem *begin() const _NOEXCEPT
	{	// get beginning of list
		return (_First);
	}

	_CONST_FUN const _Elem *end() const _NOEXCEPT
	{	// get end of list
		return (_Last);
	}

	_CONST_FUN size_t size() const _NOEXCEPT
	{	// get length of list
		return ((size_t)(_Last - _First));
	}

private:
	const _Elem *_First;
	const _Elem *_Last;
};

// TEMPLATE FUNCTION begin
template<class _Elem> inline // 将参数类型改为my_init_list
_CONST_FUN const _Elem *begin(my_init_list<_Elem> _Ilist) _NOEXCEPT
{	// get beginning of sequence
	return (_Ilist.begin());
}

// TEMPLATE FUNCTION end
template<class _Elem> inline // 将参数类型改为my_init_list
_CONST_FUN const _Elem *end(my_init_list<_Elem> _Ilist) _NOEXCEPT
{	// get end of sequence
	return (_Ilist.end());
}
也就是说代码是一样的,只有类名不同,{}也不能匹配到除initializer_list之外的类[/quote] 编译器特殊处理是因为标准要求的,标准不管,谁给你特殊处理啊,别搞反了。 {...} 的语法叫 list initialization,标准要求先匹配 std::initializer_list 的重载,如果有的话。 你自己的私货进不了标准,当然编译器不屌了。 ps. 你学 c++ 的思路搞反了,正确的途径是学习标准,研究具体实现只能起辅助作用。本末倒置可能会学歪了,到时候功夫没少出,水平却不行。
几罗星人 2017-01-22
  • 打赏
  • 举报
回复
实际上我一直在思考一个问题:当一种语言给出一种特性的时候,我们能不能用这个语言本身去实现它。 {}和initializer_list的关联是一个例子;还有学习java的时候,foreach的语法for(object o : target)可以遍历target,对于数组可以,对于内置的map也可以,那能否自己写一个类使之可以作为target呢?查了文档之后发现可以,实现Iterable接口就可以了。 我在想能否用语言本身实现给出的特性,可能是语言的一种性质。
几罗星人 2017-01-22
  • 打赏
  • 举报
回复
引用 5 楼 ri_aje 的回复:
编译器特殊处理是因为标准要求的,标准不管,谁给你特殊处理啊,别搞反了。 {...} 的语法叫 list initialization,标准要求先匹配 std::initializer_list 的重载,如果有的话。 你自己的私货进不了标准,当然编译器不屌了。 ps. 你学 c++ 的思路搞反了,正确的途径是学习标准,研究具体实现只能起辅助作用。本末倒置可能会学歪了,到时候功夫没少出,水平却不行。
谢谢,也就是说你也认同是编译器做了特殊处理,自己是不可能实现这种关联的?
几罗星人 2017-01-22
  • 打赏
  • 举报
回复
引用 7 楼 zhao4zhong1 的回复:
“变量赋值”和“变量初始化”不是一回事! “变量赋值”发生在运行期,其写法遵循赋值语法规定。 “变量初始化”发生在编译期或运行期,其写法遵循初始化列表语法规定。
赵4老师的这个回复跟题目有什么关系?我的疑惑只是{}是如何跟initializer_list联系起来的?
加载更多回复(4)

65,187

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧