2017-03-03 1 views
17
#include <vector> 
#include <iostream> 

using namespace std; 

int main() 
{ 
    vector<int> coll; 

    decltype(std::begin(std::declval<vector<int>>())) 
     pos_1 = coll.begin(); 
    auto pos_2 = coll.begin(); 

    cout << typeid(decltype(pos_1)).name() << endl; 
    cout << typeid(decltype(pos_2)).name() << endl; 
} 

Mon compilateur est clang 4.0. La sortie est:Pourquoi "std :: begin()" renvoie-t-il toujours "const_iterator" dans un tel cas?

class std::_Vector_const_iterator<class std::_Vector_val<struct std::_Simple_types<int> > > 
class std::_Vector_iterator<class std::_Vector_val<struct std::_Simple_types<int> > > 

Cela signifie: pos_1 = pos_2; est ok, alors que pos_2 = pos_1; n'est pas correct.

Pourquoi est-ce que std::begin() renvoie toujours const_iterator plutôt que iterator dans un tel cas?

+1

Je pense qu'il est parce qu'il est parce que vous êtes déduisant le type de 'l'aide d'un pos_1' temporaire. Ils se lient uniquement aux références const et tout ça, c'est là que le const vient dans le jeu. – Borgleader

Répondre

21

non-const L'appel de fonction:

std::declval<std::vector<int>>() 

se traduit par une expression rvalue qui peut être désigné par:

std::vector<int>&& 

Le compilateur a deux surcharges (générique) de std::begin à choisir ([iterator.range]):

template <class C> 
auto begin(C& c) -> decltype(c.begin());  // #1 

template <class C> 
auto begin(const C& c) -> decltype(c.begin()); // #2 

Pour une expression rvalue, seule la seconde surcharge (# 2) est viable - un rvalue ne peut pas être lié par un référence de lvalue non-const. La qualification const du type référencé implique que le compilateur utilisera la surcharge admissible const de la fonction membre begin:

const_iterator begin() const noexcept; 
//      ~~~~^ 

qui renvoie une instance de type const_iterator.

Vous pouvez modifier ce comportement en demandant une expression lvalue de std::vector<int> de l'appel std::declval:

decltype(std::begin(std::declval<std::vector<int>&>())) pos_1 = coll.begin(); 
//            ~~^~~  
+2

Intéressant à noter: si la version membre non -static' de 'begin' avait été utilisée, la version mutable aurait été préférée, retournant' iterator'. C'est une façon dont les fonctions membres et non membres diffèrent – KABoissonneault

+0

@KABoissonneault Vous voulez dire 'std :: declval >(). Begin()'? Oui, c'est à cause de [\ [over.match.funcs \] /p5.1] (http://eel.is/c++draft/over.match.funcs#5.1). –

3

si vous avez Type&& (temporaire), puis la surcharge résolution préférera const Type& sur Type&, puisque une liaison coutume temporaire à un lvalue référence