Подтвердить что ты не робот

Почему `` 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*, который явно является итератором с произвольным доступом.

4b9b3361

Ответ 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 создается неоднократно. Что опять же, конечно, вызывает удивление, почему это не было предусмотрено самим стандартом.