Why Isn't There an Operator[] for a Std::List

Why isn't there an operator[] for a std::list?

Retrieving an element by index is an O(n) operation for linked list, which is what std::list is. So it was decided that providing operator[] would be deceptive, since people would be tempted to actively use it, and then you'd see code like:

 std::list<int> xs;
for (int i = 0; i < xs.size(); ++i) {
int x = xs[i];
...
}

which is O(n^2) - very nasty. So ISO C++ standard specifically mentions that all STL sequences that support operator[] should do it in amortized constant time (23.1.1[lib.sequence.reqmts]/12), which is achievable for vector and deque, but not list.

For cases where you actually need that sort of thing, you can use std::advance algorithm:

int iter = xs.begin();
std::advance(iter, i);
int x = *iter;

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

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.

Subscript a std::list

The standard library only provides the subscript operator when it has O(1) runtime.

For std::list, the library requires you to get an iterator and use std::advance to acknowledge the performance hit.

no match for operator '=' (std::arrayT, 3 and std::initializer_listT)

Why can't I initialize std::array with initializer list in such a way?

Braced-init-list and std::initializer_list are not the same thing. (Even std::initializer_list could be constructed from braced-init-list.) std::array is an aggregate and could be aggregate-initialized or assigned by braced-init-list as

std::array<int, 3> a = {1, 2, 3};
a = {4, 5, 6}; // convert {4, 5, 6} to std::array then assign to a

Note that both {1, 2, 3} and {4, 5, 6} are braced-init-list but not std::initializer_list.

std::array can't be initialized (or assigned) from an std::initializer_list; its constructors and assignment operator are implicitly-defined and doesn't have such constructor or assignment operator taking std::initializer_list.

std::initializer_list<int> l = {1, 2, 3};
std::array<int, 3> a = l; // doesn't work
a = l; // doesn't work

Why is there no [] operator for std::shared_ptr?

std::unique_ptr only defines operator[] in a specialization for arrays: std::unique_ptr<T[]>. For non-array pointers, the operator[] doesn't make much sense anyways (only [0]).

Such a specialization for std::shared_ptr is missing (in C++11), which is discussed in the related question: Why isn't there a std::shared_ptr<T[]> specialisation?

You should not use a non-array smart pointer with array allocation, unless you provide a custom deleter. In particular, unique_ptr<int> p = new int[10] is bad, since it calls delete instead of delete[]. Use unique_ptr<int[]> instead, which calls delete[]. (And this one implements operator[]). If you're using shared_ptr to hold a T[], you need to use a custom deleter. See also shared_ptr to an array : should it be used? -- but it doesn't provide operator[], since it uses type erasure to distinguish between array and non-array (the smart pointer type is independent of the provided deleter).

If you wonder why there is no shared_ptr specialization for arrays: that was a proposal, but wasn't included in the standard (mainly since you can work around by writing ptr.get() + i for ptr[i]).

Why isn't the [] operator const for STL maps?

For std::map and std::unordered_map, operator[] will insert the index value into the container if it didn't previously exist. It's a little unintuitive, but that's the way it is.

Since it must be allowed to fail and insert a default value, the operator can't be used on a const instance of the container.

http://en.cppreference.com/w/cpp/container/map/operator_at

No match operators[]

Error messages like no match for mean, that either the method or operator isn't there at all, or there is such a method, but with different parameter types.

Looking at std::list, you can see, that this is the first case, no operator[] for std::list can be found.


Just in case, v[1] is not the first element in the list, but v[0] is. Also v[n] is not the last element.

And if the intention of the code is to calculate the sum of i * min(first, last), you might try using std::list::front() and std::list::back() instead

for (long long i = 1; i <= n; ++i) {
if (v.front() > v.back()) {
res += i * v.back();
v.pop_back();
} else {
res += i * v.front();
v.pop_front();
}
}

If you want or must use operator[] anyway, I would rather use std::deque. This container allows both random access (operator[]) and also provides methods pop_back() and pop_front().

Error: no match for operator[]. While comparing int to int in listint,

There are two problems:

  1. std::list does not have an indexing operation.
  2. A range-based loop iterates over a container's elements, not its indices.

You should almost never use std::list - std::vector is almost always the appropriate alternative - and your loop should look like this:

for (auto& element: myBinaryList){
if (element == 1){
element = 0;
}
}

Why isn't vector::operator[] implemented similar to map::operator[]?

Quite simply: Because it doesn't make sense. What do you expect

std::vector<int> a = {1, 2, 3};
a[10] = 4;

to do? Create a fourth element even though you specified index 10? Create elements 3 through to 10 and return a reference to the last one? Neither would be particularily intuitive.

If you really want to fill a vector with values using operator[] instead of push_back, you can call resize on the vector to create the elements before settings them.

Edit: Or, if you actually want to have an associative container, where the index is important apart from ordering, std::map<int, YourData> might actually make more sense.



Related Topics



Leave a reply



Submit