Jak działa funkcja std::move ?

0

Witam !
Chciałbym się zapytać dlaczego po wykonaniu vec_str.push_back(move(str)); string "str" jest pusty a obiekt typu Test* "t" po vec.push_back(move(t)); nie ? Myślałem że funkcja std::move przenosi zmienną i jej poprzednie miejsce w pamięci jest puste.

#include <iostream>
#include <string>
#include <vector>

using namespace std;

struct Test
{
	int& value;
	Test(int& val): value{val}{}
};

int main()
{
	int value = 100;
	Test* t = new Test(value);

	vector<Test*> vec;
	
	vec.push_back(t);
	cout << t->value << endl;

	vec.push_back(move(t));
	cout << t->value << endl;

	string str = "Hello";
	vector<string> vec_str;
	vec_str.push_back(move(str));
	cout << str << endl;
}

1

Tutaj jest całkiem fajnie wytłumaczone: https://devcode.pl/cpp11-semantyka-przeniesienia/

3

std::move nic nie robi. Zmienia tylko typ z typu l-value na typ r-value.
Od tego miejsca overload resolution przejmuje kontrolę.

std::string obsługuje move semantics ponieważ może trzymać dane na stercie i przekazanie własności do pamięci jest tańsze niż kopiowanie.
Test* jest trywialnie kopiowany (nie ma sensu dostarczać move).

0

Czyli to 'vector<string> vec_str zwalnia pamięć po zmiennej string str ?

Dzięki za pomoc !

1

Rozwinięcie tego co napisał @MarekR22.

  • Dla typów wbudowanych (int, double, wskaźnik itd.) przeniesienie jest równoważne kopiowaniu.
  • Gdy deklarujesz string str = "hello", Twoja zmienna str zajmuje miejsce zarówno na stosie jak i na stercie. Na stosie jest sam obiekt str, natomiast na stercie jest pamięć na napis "hello".
  • Gdy robisz std::move(str), obiekt str przestaje być właścicielem tej pamięci na stercie i nie będzie już zajmować się zwalnianiem tej pamięci. Obiekt str nadal istnieje na stosie i będzie automatycznie zwalniany gdy str wyjdzie poza zakres (w tym przypadku pod koniec main()).

Najlepszy tutorial o move semantics, r-value reference i perfect forwarding jaki widziałem w necie: http://thbecker.net/articles/rvalue_references/section_01.html
Z tego tutoriaal dowiesz się m.in. że std::move może być zaimplementowany jako

template<class T> 
typename remove_reference<T>::type&&
std::move(T&& a) noexcept
{
  typedef typename remove_reference<T>::type&& RvalRef;
  return static_cast<RvalRef>(a);
} 

czyli to jest po prostu static_cast.

0
string str = "Hello";
vector<string> vec_str;
vec_str.push_back(move(str));

To jest właściwie to samo co:

string str = "Hello";
vector<string> vec_str;
vec_str.push_back((string&&)str);

Nie sprawdzane.

Jedną z dziwnych reguł C++ jest, że nazwana zmienna nigdy nie jest r-value reference, czyli dowolne foo nie jest typu klasa&& nawet jeśli zadeklarowano klasa &&foo.

r-value reference może stanowić np. wyrażenie, czy wynik wywołania funkcji – ogólniej „wartość tymczasowa”. Jeśli chcemy żeby nazwane foo zachowało się jak klasa&&, to trzeba użyć np. rzutowania (czyli sprawić że będzie to wyrażenie, a nie samo użycie zmiennej).
Dla „ułatwienia” wymyślono takie castujące funkcje (move i forward), które jednak same w sobie wbrew nazwie niczego nie robią (czyli np. move(x); nie ma absolutnie żadnych skutów) ale wpływają na to, który overload funkcji zostanie wybrany.

1 użytkowników online, w tym zalogowanych: 0, gości: 1