如果std::initializer_list
中的元素始终是const值,那么我们为什么会有像begin()/end()
这样的模板方法而不是cbegin()/cend()
?这些名称(按照约定,与例如std::vector
相比)可能表明,两种std::initializer_list
方法都可以返回iterator
,但它们始终返回const_iterator
。
如果std::initializer_list
中的元素始终是const值,那么我们为什么会有像begin()/end()
这样的模板方法而不是cbegin()/cend()
?这些名称(按照约定,与例如std::vector
相比)可能表明,两种std::initializer_list
方法都可以返回iterator
,但它们始终返回const_iterator
。
尽管我无法解释为什么cbegin()
和cend()
没有作为std::initializer_list
接口的一部分附加begin()
和end()
,但有充分的理由说明为什么应该存在最后两个成员函数。
例如,其中一个原因是基于范围的 for
循环是由C++11标准精确地定义为函数begin()
和end()
(第6.5.4/1段)。因此,为了使其能够与初始化列表一起使用,std::initializer_list
必须提供begin()
和end()
成员函数:
#include <utility>
#include <iostream>
int main()
{
auto l = { 1, 2, 3, 4, 5 };
for (int x : l) // Works because std::initializer_list provides
// the member functions begin() and end().
{
std::cout << x << " ";
}
}
此外,考虑到成员函数 cbegin()
和 cend()
在 C++11 之前不存在, 因此在 std::initializer_list
接口中有 begin()
和 end()
允许旧的通用算法在不需要重写的情况下也可以使用初始化列表,而这些算法是按照 begin()
和 end()
这样的术语编写的。
你写道:
通过比较例如
std::vector
,这些名称(按约定)可能会暗示两个方法都会返回iterator
,但实际上它们总是返回const_iterator
。
实际上,这个类比并不十分恰当。例如,对于一个非const
的std::vector
(即可修改、添加和删除其元素的可变实例),begin()
函数会返回一个iterator
,而对于一个const
的实例(即其内容不能被更改的不可变实例),begin()
函数会返回一个const_iterator
:
#include <vector>
#include <type_traits>
int main()
{
// A non-const vector...
std::vector<int> v = { 1, 2, 3, 4, 5 };
auto i = v.begin();
static_assert(
std::is_same<decltype(i), decltype(v)::iterator>::value,
// ^^^^^^^^
// ...non-const iterator!
"What?");
// A const vector...
std::vector<int> const vc = { 1, 2, 3, 4, 5 };
auto ic = vc.begin();
static_assert(
std::is_same<decltype(ic), decltype(vc)::const_iterator>::value,
// ^^^^^^^^^^^^^^
// ...const iterator!
"What?");
}
按照C++11标准第18.9/2段的规定,初始化器列表是不可变集合:
类型为
initializer_list<E>
的对象提供对类型为const E
的对象数组的访问。[...]
由于初始化器列表是由const
元素组成的集合,因此cbegin()
和函数实际上执行与begin()
和end()
相同的操作。
事实上,iterator
和 const_iterator
都定义为指向初始化器列表值类型的常量元素的指针,因此可以争论begin()
和end()
是否总是返回const_iterator
(如你所假设)或始终返回iterator
。
C++11标准的第18.9/1段规定了initializer_list
类模板:
namespace std {
template<class E> class initializer_list {
public:
typedef E value_type;
// ...
typedef const E* iterator;
typedef const E* const_iterator;
// ...
constexpr const E* begin() const noexcept; // first element
constexpr const E* end() const noexcept; // one past the last element
};
// ...
}