2017-03-03 96 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; 
} 

Mój kompilator to clang 4.0. Wyjście jest:Dlaczego "std :: begin()" zawsze zwraca "const_iterator" w takim przypadku?

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

To znaczy: pos_1 = pos_2; jest ok, natomiast pos_2 = pos_1; nie jest w porządku.

Dlaczego w takim przypadku std::begin() zawsze zwraca const_iterator zamiast iterator?

+1

Domyślam się, że to dlatego, że to dlatego, że jesteś wnioskowanie typu 'pos_1' korzystając tymczasowy. Wiążą się tylko ze stałymi referencjami i tym wszystkim, w którym pojawia się konsta w grze. – Borgleader

Odpowiedz

21

Wywołanie funkcji:

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

skutkuje ekspresją rValue, które mogą być oznaczone jako:

std::vector<int>&& 

Kompilator ma dwa (ogólne) przeciążeniem std::begin do wyboru ([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 

Dla wyrażenia rvalue, tylko drugi przeciążenie (# 2) jest opłacalne - rvalue nie może być związany przez niezwiązane odniesienie do lwartości. Const kwalifikacje odwołanie typu powoduje, że kompilator będzie używać const wykwalifikowanym przeciążenia funkcji begin użytkownika:

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

która zwraca instancję typu const_iterator.

Możesz zmienić to zachowanie żądając lwartością ekspresji std::vector<int> z połączenia std::declval:

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

Interesujące, aby zwrócić uwagę: jeśli użyto innej niż "statycznej" wersji 'begin', preferowana byłaby wersja zmienna, zwracająca' iterator'. Jest to funkcja jednokierunkowa i funkcje nie będące członkami różnią się – KABoissonneault

+0

@KABoissonneault Masz na myśli 'std :: declval >(). Begin()'? Tak, to z powodu [\ [over.match.funcs \] /p5.1] (http://eel.is/c++draft/over.match.funcs#5.1). –

3

jeśli masz Type&& (tymczasowego), to przeciążenie rozdzielczości wolą const Type& nad Type&, ponieważ tymczasowy przyzwyczajenie wiążą się z non-const lwartości odniesieniem