Если элементы в std::initializer_list
всегда являются константными значениями, почему у нас есть шаблонный метод, например begin()/end()
, а не cbegin()/cend()
? Эти имена (по соглашениям, по сравнению с, например, std::vector
) могут предполагать, что оба метода std::initializer_list
могут возвращать iterator
, когда они всегда возвращают const_iterator
.
Std:: initializer_list без cbegin()/cend()
Ответ 1
Пока я не могу представить, почему cbegin()
и cend()
не являются частью интерфейса std::initializer_list
в дополнение к begin()
и end()
, есть, конечно, веские причины, по которым последние два функции-члены должны быть там.
Одной из причин является, например, то, что цикл for
на основе диапазона определен стандартом С++ 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()
не присутствовали до С++ 11: поэтому наличие begin()
и end()
на интерфейсе std::initializer_list
позволяет создавать старые общие алгоритмы, написанные в терминах begin()
и end()
, также работают с списками инициализаторов, не требуя их перезаписи.
Вы пишете:
Эти имена (по соглашениям, по сравнению с, например,
std::vector
) могут предполагать, что оба методаstd::initializer_list
могут возвращатьiterator
, когда они всегда возвращаютconst_iterator
.
Собственно, эта аналогия не очень уместна. std::vector
Функция begin()
, например, возвращает iterator
при вызове в экземпляре const
std::vector
(т.е. изменчивом, элементы которого могут быть изменены, добавлены и удалены) и const_iterator
при вызове в экземпляре const
(т.е. неизменяемом, содержимое которого не может быть изменено):
#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?");
}
Списки инициализаторов по определению являются неизменяемыми коллекциями. В абзаце 18.9/2 стандарта С++ 11:
Объект типа
initializer_list<E>
обеспечивает доступ к массиву объектов типаconst E
. [...]
Так как списки инициализаторов представляют собой коллекции элементов const
, функции cbegin()
и cend()
фактически выполняют ту же самую вещь, что и begin()
и end()
.
Фактически, iterator
и const_iterator
определяются как указатели на постоянные элементы типа значения типа инициализатора, поэтому можно утверждать, что в случае begin()
и end()
всегда возвращаются const_iterator
( как вы предполагаете), или они всегда возвращают iterator
.
Вот как параграф 18.9/1 стандарта С++ 11 определяет шаблон класса 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
};
// ...
}