为何基于范围的for循环支持初始化列表,cbegin却不支持?
这个问题的核心在于基于范围的for循环和std::cbegin对花括号初始化列表的处理规则存在差异,咱们一步步拆解来理解:
1. 基于范围的for循环的特殊处理
C++标准对基于范围的for循环做了专门的规定:当循环的"范围"是一个花括号初始化列表时,编译器会自动将其隐式转换为std::initializer_list<int>类型(这里的int是根据列表元素推导出来的)。
换句话说,你写的这段代码:
for(const auto i : { 13, 42 }) cout << i << ' ';
编译器会在背后帮你完成等价逻辑:
const std::initializer_list<int> temp_list = {13,42}; for(const auto i : temp_list) cout << i << ' ';
所以基于范围的for循环能直接识别并处理花括号初始化列表。
2. std::cbegin的模板推导限制
std::cbegin是一个模板函数,它的工作依赖于模板参数推导——编译器需要从传入的参数中推导出对应的模板类型,才能匹配到正确的函数重载。
但单独的{13,42}只是一个花括号初始化列表,它本身并不是一个具有明确类型的对象,只是一种语法结构。模板推导无法直接从这个语法结构中推导出它应该对应std::initializer_list<int>,因此编译器会报错:
error: no matching function for call to
cbegin(<brace-enclosed initializer list>)
3. 显式指定类型为什么能解决问题
当你显式写出initializer_list<int>{13,42}时,你已经明确创建了一个std::initializer_list<int>类型的对象。此时std::cbegin可以识别这个类型(因为std::initializer_list提供了对应的cbegin成员函数,或者std::cbegin有针对该类型的重载),自然就能正常编译运行了:
copy_n(cbegin(initializer_list<int>{ 13, 42 }), 2, ostream_iterator<int>(cout, " "));
补充:同类问题的共性
其实这不是std::cbegin独有的问题,所有依赖模板参数推导的函数,直接传入花括号初始化列表都会遇到类似的编译失败。比如你定义一个简单的模板函数:
template<typename T> void func(T t) {}
调用func({1,2})会编译失败,但func(std::initializer_list<int>{1,2})就可以正常运行——本质都是因为模板推导无法直接处理无类型的花括号初始化列表。
内容的提问来源于stack exchange,提问作者Jonathan Mee

