Почему `std::initializer_list` не предоставляет оператор индекса?

Предположим, вы пишете функцию, которая принимает std::initializer_list с именем list, и эта функция требует произвольного доступа к элементам list. Было бы удобно писать list[i] вместо list.begin()[i]. Так почему же std::initializer_list не дает определения operator[]?

Я не могу вспомнить ни одного случая, когда operator[], возвращающее const T&, не было бы четко определено. Эффективность здесь, похоже, не проблема, поскольку std::initializer_list<T>::iterator имеет псевдоним const T*, который явно является итератором произвольного доступа.


person void-pointer    schedule 22.07.2013    source источник
comment
Я предполагаю, что его основной вариант использования - это список, который обрабатывается последовательно.   -  person PlasmaHH    schedule 22.07.2013
comment
Обычный вариант использования — конструктор, который выделяет блок памяти и создает элементы в блоке, используя allocator.construct(p, v). В то время как список все еще обрабатывается последовательно, внешний цикл for уже имеет счетчик, который поддается синтаксису operator[].   -  person void-pointer    schedule 22.07.2013
comment
@void-pointer Разве вы не стали бы копировать в таких обстоятельствах? Таким образом, вам не нужен цикл, и я бы в любом случае избегал явного цикла. Хотя вопрос по-прежнему актуален.   -  person Konrad Rudolph    schedule 22.07.2013
comment
@void-pointer: вы можете просто написать этот цикл и с итераторами, и, конечно же, есть некоторые алгоритмы, которые вы тоже можете использовать.   -  person PlasmaHH    schedule 22.07.2013
comment
@KonradRudolph Хороший вопрос, uninitialized_copy сделал бы эту работу более элегантно. Брб, придется рефакторить код =)   -  person void-pointer    schedule 22.07.2013
comment
Как догадка, первая итерация в стандарте. Будь проще? Забавно, но .begin()[N] работает, если begin случайное.   -  person Yakk - Adam Nevraumont    schedule 22.07.2013


Ответы (2)


Согласно Бьерну Страуструпу в разделе 17.3.4.2 (стр. 497) языка программирования С++, 4-е издание:

К сожалению, initializer_list не поддерживает подписку.

Больше причин не приводится.

Я предполагаю, что это одна из этих причин:

  1. это упущение или
  2. поскольку класс initializer_list реализован с помощью массива, и вам пришлось бы выполнять проверку границ, чтобы обеспечить безопасный доступ, его было бы легче использовать небезопасно, если бы этот интерфейс был предоставлен, или
  3. чтобы соответствовать парадигме итерации стандартных алгоритмов, или
  4. поскольку списки initializer_lists по своей природе являются специальными, есть больше места для ошибок, если обратиться к ним напрямую.

2 и 4 звучат как-то слабо. как и 3. Мои деньги на 1.

person Homer6    schedule 24.07.2013
comment
6 лет спустя, знаем ли мы ответ? - person jippyjoe4; 07.08.2019

Это действительно немного раздражает, отсутствие оператора квадратных скобок для 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);
}

Теперь у нас есть новая глобальная функция с именем _ (подчеркивание), которое, вероятно, не является хорошим именем для глобального метода C++, если только мы не хотим создать какую-нибудь "подобную подчеркиванию" служебную библиотеку для C++, которая будет иметь собственное пространство имен, перегружая _ для всех других полезных применений.

Новую функцию _ теперь можно использовать следующим образом:

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

person Amir Kirsh    schedule 09.10.2015
comment
Это мило. Альтернативой, перехватывающей комментарий @Yakk к вопросу, является auto mylist = list.begin(), и тогда mylist[n] работает. - person Samuel Danielson; 03.03.2016
comment
Если бы я увидел синтаксис в коде, я бы сразу стал wtf. Я бы не знал, что _ на самом деле является идентификатором, а не функцией. Как рли, втф. Я бы, вероятно, понял это в конце концов (работает ли определение для идентификатора с именем _ ??), но это ничего не сделает, чтобы облегчить мои интенсивные, быстрые и повторяющиеся упоминания семьи кодера, который это написал. Мой совет просто пойти с list.begin()[i]. Прошу прощения за понижение, потому что это крутой трюк с точки зрения языка, но на практике это будет иметь неприятные последствия. - person bolov; 03.10.2016
comment
Я думаю, вы действительно хотите вернуть cons T& вместо копии. - person MikeMB; 21.06.2017
comment
@bolov оригинальный wtf - это упущение. люди никогда не вернутся к cpp с такими заминками. - person Anona112; 22.03.2018