Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::initializer_list without cbegin()/cend()

If elements in std::initializer_list are always const values, why we have template method like begin()/end() and not cbegin()/cend()? This names (by conventions, comparing to e.g. std::vector) could suggest that both std::initializer_list method could return iterator, when they always return const_iterator.

like image 519
NiegodziwyBeru Avatar asked Mar 25 '13 19:03

NiegodziwyBeru


1 Answers

While I can't provide an insight on the reason why cbegin() and cend() are not part of std::initializer_list's interface in addition to begin() and end(), there are certainly good reasons why the last two member functions ought to be there.

One reason is, for instance, that the range-based for loop is defined by the C++11 Standard precisely in terms of functions begin() and end() (Paragraph 6.5.4/1). Therefore, in order to make it possible to use it with initializer lists, std::initializer_list has to provide the begin() and end() member functions:

#include <utility>
#include <iostream>

int main()
{
    auto l = { 1, 2, 3, 4, 5 };
    for (int x : l) // Works because std::initializer_list provides
                    // the member functions begin() and end().
    {
        std::cout << x << " ";
    }
}

Moreover, it makes sense to consider that member functions cbegin() and cend() were not present before C++11: therefore, having begin() and end() on the interface of std::initializer_listallows making old generic algorithms written in terms of begin() and end() work with initializer lists as well, without requiring them to be rewritten.

You write:

These names (by conventions, comparing to e.g. std::vector) could suggest that both std::initializer_list method could return iterator, when they always return const_iterator.

Actually, this analogy is not very appropriate. std::vector's function begin(), for instance, returns an iterator when invoked on a non-const instance of std::vector (i.e. a mutable one, whose elements can be modified, added, and removed), and a const_iterator when invoked on a const instance (i.e. an immutable one, whose content cannot be altered):

#include <vector>
#include <type_traits>

int main()
{
    // A non-const vector...
    std::vector<int> v = { 1, 2, 3, 4, 5 };

    auto i = v.begin();
    static_assert(
        std::is_same<decltype(i), decltype(v)::iterator>::value, 
        //                                     ^^^^^^^^
        //                                     ...non-const iterator!
        "What?");

    // A const vector...
    std::vector<int> const vc = { 1, 2, 3, 4, 5 };
    auto ic = vc.begin();
    static_assert(
        std::is_same<decltype(ic), decltype(vc)::const_iterator>::value,
        //                                       ^^^^^^^^^^^^^^
        //                                       ...const iterator!
        "What?");
}

Initializer lists are immutable collections by definition. Per Paragraph 18.9/2 of the C++11 Standard:

An object of type initializer_list<E> provides access to an array of objects of type const E. [...]

Since initializer lists are collections of const elements, the cbegin() and cend() functions would actually do the exact same thing that begin() and end() do.

In fact, iterator and const_iterator are both defined as pointers to constant elements of the initializer list's value type, so it's arguable whether it is the case that begin() and end() always return const_iterator (as you assume), or whether they always return iterator.

This is how Paragraph 18.9/1 of the C++11 Standard defines the initializer_list class template:

namespace std {
    template<class E> class initializer_list {
    public:
        typedef E value_type;
        // ...
        typedef const E* iterator;
        typedef const E* const_iterator;
        // ...
        constexpr const E* begin() const noexcept; // first element
        constexpr const E* end() const noexcept; // one past the last element
    };

    // ...
}
like image 62
Andy Prowl Avatar answered Nov 20 '22 16:11

Andy Prowl