23 votes

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

Si des éléments dans std::initializer_list sont toujours des valeurs constantes, pourquoi avons-nous une méthode modèle comme begin()/end() et non cbegin()/cend() ? Ce nom (par convention, en comparaison avec par exemple std::vector ) pourrait suggérer que les deux std::initializer_list pourrait retourner iterator alors qu'ils reviennent toujours const_iterator .

26voto

Andy Prowl Points 62121

Bien que je ne puisse pas fournir un aperçu de la raison pour laquelle cbegin() y cend() ne font pas partie de std::initializer_list L'interface de l'entreprise en plus de begin() y end() il y a certainement de bonnes raisons pour lesquelles les deux dernières fonctions membres devraient être présentes.

L'une des raisons est, par exemple, que l'approche basée sur la gamme for La boucle est définie par la norme C++11 précisément en termes de fonctions begin() y end() (Paragraphe 6.5.4/1). Par conséquent, pour qu'il soit possible de l'utiliser avec des listes d'initialisateurs, std::initializer_list doit fournir le begin() y end() les fonctions des membres :

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

En outre, il est logique de considérer que les fonctions membres cbegin() y cend() n'étaient pas présents avant C++11 : par conséquent, avoir des begin() y end() sur l'interface de std::initializer_list permet de rendre les anciens algorithmes génériques écrits en termes de begin() y end() fonctionnent également avec les listes d'initialisation, sans nécessiter leur réécriture.

Vous écrivez :

Ces noms (par convention, en se comparant à par exemple std::vector ) pourrait suggérer que les deux std::initializer_list pourrait retourner iterator alors qu'ils reviennent toujours const_iterator .

En fait, cette analogie n'est pas très appropriée. std::vector La fonction de l'entreprise begin() par exemple, renvoie un iterator lorsqu'il est invoqué sur un non const exemple de std::vector (c'est-à-dire mutable, dont les éléments peuvent être modifiés, ajoutés et supprimés), et une const_iterator lorsqu'il est invoqué sur un const (c'est-à-dire une instance immuable, dont le contenu ne peut être modifié) :

#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?");
}

Les listes d'initialisation sont des collections immuables par définition. Paragraphe 18.9/2 de la norme C++11 :

Un objet de type initializer_list<E> permet d'accéder à un tableau d'objets de type const E . [...]

Puisque les listes d'initialisation sont des collections de const les éléments cbegin() y cend() ferait en fait exactement la même chose que begin() y end() faire.

En fait, iterator y const_iterator sont toutes deux définies comme des pointeurs vers des éléments constants du type de valeur de la liste d'initialisation, on peut donc se demander si c'est le cas que begin() y end() toujours revenir const_iterator (comme vous le supposez), ou si elles retournent toujours iterator .

C'est ainsi que le paragraphe 18.9/1 de la norme C++11 définit l'approche de l'utilisateur. initializer_list modèle de classe :

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

    // ...
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X