Почему `` std:: initializer_list` не предоставляет оператора индексирования?
Предположим, что вы пишете функцию, которая принимает std::initializer_list
под названием list
и что для функции требуется произвольный доступ к элементам list
. Было бы удобно писать list[i]
вместо list.begin()[i]
. Итак, почему std::initializer_list
не предоставляет определение operator[]
?
Я не могу думать о каких-либо случаях, когда возвращаемый const T&
const T&
не был бы корректно определен. Эффективность здесь, похоже, не проблема, так как std::initializer_list<T>::iterator
имеет псевдоним const T*
, который явно является итератором с произвольным доступом.
Ответы
Ответ 1
Согласно Bjarne Stroustrup в разделе 17.3.4.2 (стр. 497) языка программирования С++, 4-е издание:
К сожалению, initializer_list не предоставляет подписки.
Дальнейшая причина не указана.
Я предполагаю, что это одна из следующих причин:
- это упущение или
- потому что класс initializer_list реализован с массивом, и вам нужно будет выполнить проверку границ для обеспечения безопасного доступа, его можно было бы более легко использовать, если бы этот интерфейс был предоставлен, или
- чтобы соответствовать парадигме итераций алгоритмов std, или
- потому что initializer_lists по своей природе являются ad-hoc, там больше места для ошибки, обращаясь к ним напрямую.
2 и 4 кажутся слабыми. как и 3. Мои деньги на 1.
Ответ 2
Это действительно немного раздражает, не имея оператора квадратных скобок для std:: initializer_list, так как необходимость случайного прямого доступа для конкретного индекса является разумным сценарием.
Однако эту способность можно добавить с помощью простого кода:
// the class _init_list_with_square_brackets provides [] for initializer_list
template<class T>
struct _init_list_with_square_brackets {
const std::initializer_list<T>& list;
_init_list_with_square_brackets(const std::initializer_list<T>& _list): list(_list) {}
T operator[](unsigned int index) {
return *(list.begin() + index);
}
};
// a function, with the short name _ (underscore) for creating
// the _init_list_with_square_brackets out of a "regular" std::initializer_list
template<class T>
_init_list_with_square_brackets<T> _(const std::initializer_list<T>& list) {
return _init_list_with_square_brackets<T>(list);
}
Теперь у нас есть новая глобальная функция с именем _ (underscore), которая, вероятно, не является хорошим именем для глобального метода С++, если мы не хотим создать некоторую "утилиту undescore" для С++, которая будет иметь собственное пространство имен, перегружая функцию _ для всех видов полезных применений.
Теперь новую функцию можно использовать следующим образом:
void f(std::initializer_list<int> list) {
cout << _(list)[2]; // subscript-like syntax for std::initializer_list!
}
int main() {
f({1,2,3}); // prints: 3
cout << _({1,2,3})[2]; // works also, prints: 3
return 0;
}
Следует отметить, что приведенное выше решение не является хорошей сделкой с точки зрения производительности, если вы используете много элементов std:: initializer_list, поскольку временный объект предлагаемого типа выше _init_list_with_square_brackets
создается неоднократно. Что опять же, конечно, вызывает удивление, почему это не было предусмотрено самим стандартом.