2015-12-11 18 views
5
template <typename T> 
void myswap(T a,T b) 
{ 
    T temp = a; 
    a = b; 
    b = temp; 
} 
int main() 
{ 
    int m(20),n(30); 
    myswap(ref(m),ref(n)); 
    //m is still 20 and n is still 30 
} 

Dlaczego wartości m i n nie zostały zamienione? Przekazanie wartości owiniętej w std::ref do funkcji INCREMENT powoduje zmianę wartości w pierwotnej zmiennej (zmienna w ramce stosu, która wywołuje funkcję INCREMENT). Czy korzystanie z usługi std::ref jest ograniczone/ograniczone?std :: ref i funkcja wymiany wydaje się nie działać dobrze razem

Odpowiedz

8

std::ref (i związane z nim std::reference_wrapper) jest narzędziem zaprojektowanym do bardzo specyficznych przypadków użycia w standardowej bibliotece i powinno być używane wyłącznie w tych przypadkach; jeśli chcesz używać go w swoich własnych miejscach, musisz go dokładnie zrozumieć i szanować to, co robi.

Zasadniczo, reference_wrapper jest o wiele bliższy wskaźnikowi niż odniesienie. Więc zastąpić wskaźnik w funkcji swap, a zobaczysz, że nie ma powodu, aby zakładać, że to rzeczywiście zamienić:

void myswap(int* a, int* b) 
{ 
    int* temp = a; 
    a = b; 
    b = temp; 
} 
+0

Lub użyj 'std :: swap' – 101010

+0

Pierwsze zdanie jest nieco mylące.Oczywiście 'std :: ref' nie jest ograniczone do standardowej biblioteki (jak również zauważasz to po niej) –

1

Twój myswap wykonuje elementy przez wartości.

Zasadniczo zamieniasz dwie referencje (std::reference_wrapper s) w lokalnym zasięgu funkcji.

Wartości, na które wskazują, nie ulegną zmianie.


template <typename T> void incrementer(T a) { ++a; } int a = 20; 

W tym przypadku nie jest to konwersja do int&with:

operator T&() const noexcept { return *_ptr; } 

W kodzie, z drugiej strony:

T temp = a; 

po prostu wywołać konstruktor kopiujący, który kopiuje wskaźnik bazowy:

reference_wrapper(const reference_wrapper&) noexcept = default; 

następnie na kolejnych liniach ponownie skopiować wskaźnik:

reference_wrapper& operator=(const reference_wrapper& x) noexcept = default; 
+0

@Karoly: Przekazuję funkcję reference_wrapper do tej funkcji. nie zwykła liczba całkowita. http://stackoverflow.com/questions/26766939/difference-between-stdreference-wrapper-and-simple-pointer?lq=1 (about reference_wrapper i jego zalety). –

+0

@ 101010: Czy to nie to, co mówiłem? Pozwól mi edytować, aby było to oczywiste. –

+0

Tak, pobierają one elementy (typu std :: ref) według wartości. Ale kopia dla m i n nie jest tworzona w funkcji myswap. Dwa nowe odniesienia wciąż wskazują na m & n utworzone w przedostatniej ramie stosu. –

4

Twój kod tworzy dwa obiekty tymczasowe std::reference_wrapper i zamienia je, więc odnoszą się one do różnych obiektów. Wszystko, co się dzieje, to zamiana dwóch obiektów, a nie ich celów.

Jeśli ręcznie napisać co szablon funkcja wygeneruje Powodem zachowanie powinno być oczywiste:

void myswap(std::reference_wrapper<int> a, std::reference_wrapper<int> b) 
{ 
    std::reference_wrapper<int> temp = a; 
    a = b; 
    b = a; 
} 

Oczywiście to nie zmienia int przedmiotów, tylko reference_wrapper obiektów.

Jeśli próbujesz zrobić to, aby myswap wziął referencje, musisz zadzwonić pod numer myswap<int&>(m, n), nie można tego naśladować, używając reference_wrapper. Ale naprawdę powinieneś naprawić myswap, ponieważ jest to całkiem bezużyteczne, tak jak teraz jest napisane.

+0

Rzeczywiście, ale czy tymczasowe nie powinno wskazywać na m i n utworzone w przedostatniej ramce stosu ?. Przekaż zmienną opakowaną w std :: ref do funkcji inkrementacji, a zobaczysz zaktualizowaną wartość wud. Czy nie jest tymczasowy utworzony w funkcji przyrostu? Następnie, w jaki sposób zmiana jest odzwierciedlona w oryginalnej zmiennej. –

+0

@Jon: Tak, przyjmuje wartość "według wartości". szablon void incrementer (T) { ++ a; } int a = 20; inkrementator (ref (a)); // to 21 teraz –

+0

'reference_wrapper' nie obsługuje operatora inkrementacji, więc funkcja inkrementacji powoduje konwersję do' int & 'i zwiększa cel. Funkcja swap używa tylko konstruktora kopiowania i przypisania kopii, które 'reference_wrapper' _does_ support, więc te operacje działają bezpośrednio na' reference_wrapper', a nie na targetu. –

Powiązane problemy