简体   繁体   中英

Why doesn't `std::initializer_list` provide a subscript operator?

Suppose that you are writing a function that accepts an std::initializer_list called list , and that the function requires random access to list 's elements. It would be convenient to write list[i] instead of list.begin()[i] . So why doesn't std::initializer_list provide a definition of operator[] ?

I can't think of any cases where an operator[] returning const T& would not be well-defined. Efficiency doesn't seem to be the issue here, since std::initializer_list<T>::iterator is aliased to const T* , which is clearly a random-access iterator.

According to Bjarne Stroustrup in Section 17.3.4.2 (p. 497) of The C++ Programming Language, 4th Edition:

Unfortunately, initializer_list doesn't provide subscripting.

No further reason is given.

My guess is that it's one of these reasons:

  1. it's an omission, or
  2. because the initializer_list class is implemented with an array and you'd have to do bounds checking to provide safe access, it could more easily be used unsafely if that interface was provided, or
  3. to be consistent with the std algorithms iteration paradigm, or
  4. because initializer_lists are, by their nature, ad-hoc, there's more room for error by addressing them directly

2 and 4 sound kind of weak. as does 3. My money's on 1.

It's indeed a bit annoying, not having square brackets operator for std::initializer_list, as the need for random direct access for a specific index is a reasonable scenario.

However, this ability can be added with some simple code:

// 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);
}

Now we have a new global function named _ (underscore) which is probably not a good name for a global C++ method, unless we want to create some "undescore-like" utility lib for C++, which will have its own namespace, overloading the _ function for all sort of other useful usages.

The new _ function can be used now like this:

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;
}

It is to be noted that the solution provided above is not a good bargain in terms of performance if you run over many items of std::initializer_list, as a temporary object of the suggested type above _init_list_with_square_brackets is created repeatedly. Which again of course raises the wonder why this was not provided by the standard itself.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM