Форум программистов, компьютерный форум CyberForum.ru

std::move, rvalue reference - C++

Восстановить пароль Регистрация
 
Olivеr
 Аватар для Olivеr
411 / 407 / 13
Регистрация: 06.10.2011
Сообщений: 830
27.04.2013, 18:57     std::move, rvalue reference #1
Здравствуйте! Недавно начал разбираться с новыми способами передачи аргументов. Прочитал около 10 статей, некоторые на русском, некоторые на английском. Но осталось несколько вопросов...
Помогите разобраться.
Допустим есть класс:
C++
1
2
3
4
5
6
class Foo
{
public:
    Foo( vector<string> v): vec( v ) {}
    vector<string> vec;
};
В главной функции я создаю вектор строк и заполняю его данными для того что бы передать его в конструктор (и больше вектор мне не нужен).
C++
1
2
3
4
5
6
7
8
9
int main()
{
    vector<string> s;
    s.push_back("string 1");
    s.push_back("string 2");
    Foo f( move(s) );
    cout << boolalpha << s.empty(); //вывод true - вектор пустой, значит данные переместились
    return 0;
}
Ставиться вопрос: сколько копирований вектора происходит во время выполнения
C++
1
Foo f( move(s) );
Одно копирование? move(s) переместил содержимое в параметр конструктора v, а после из v содержимое скопировалось в член класса vec.

И еще взгляните на почти аналогичный код:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Foo
{
public:
    Foo( vector<string> v): vec( move(v) ) {} //сюда я добавил move
    vector<string> vec;
};
 
int main()
{
    vector<string> s;
    s.push_back("string 1");
    s.push_back("string 2");
    Foo f( s );
    cout << boolalpha << s.empty(); //на выводе false, то есть было два копирования (из s в v и из v в vec)
    return 0;
}
И такой вариант:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <utility>
#include <vector>
 
using namespace std;
 
class Foo
{
public:
    Foo( vector<string> v): vec( move(v) ) {} //добавил move
    vector<string> vec;
};
 
int main()
{
    vector<string> s;
    s.push_back("string 1");
    s.push_back("string 2");
    Foo f( move(s) ); //добавил move
    cout << boolalpha << s.empty(); //на выводе true
    return 0;
}
То есть, в последнем варианте не было копирования вообще?
Компилятор MinGW 4.7.3
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Nick Alte
Эксперт С++
1590 / 982 / 115
Регистрация: 27.09.2009
Сообщений: 1,897
Завершенные тесты: 1
27.04.2013, 19:14     std::move, rvalue reference #2
Цитата Сообщение от Olivеr Посмотреть сообщение
Одно копирование? move(s) переместил содержимое в параметр конструктора v, а после из v содержимое скопировалось в член класса vec.
move никуда ничего не переместил, потому что конструктор принимает не rvalue reference, а копию. Так что копирование выполняется дважды.
Цитата Сообщение от Olivеr Посмотреть сообщение
То есть, в последнем варианте не было копирования вообще?
Во втором и третьем вариантах выполняется копирование в параметр (оно выполняется всегда, потому что это указано в описании конструктора, и никакой move этого не изменит), а затем эта копия перемещается в vec.

Добавлено через 1 минуту
Как делать конструктор с перемещением:
C++
1
2
3
4
5
6
class Foo
{
public:
    Foo( vector<string>&& v): vec( move(v) ) {}
    vector<string> vec;
};
Olivеr
 Аватар для Olivеr
411 / 407 / 13
Регистрация: 06.10.2011
Сообщений: 830
27.04.2013, 19:25  [ТС]     std::move, rvalue reference #3
Цитата Сообщение от Nick Alte Посмотреть сообщение
C++
1
2
3
4
5
6
class Foo
{
public:
    Foo( vector<string>&& v): vec( move(v) ) {}
    vector<string> vec;
};
В таком случае будет прямое перемещение? Не будет копирования?

Я не просто так передавал по значению. Я видел много раз, что люди так делают. Например такой класс:
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Book {
public:
  Book(std::string title,
       std::vector<std::string> authors,
       size_t      pub_day
       std::string pub_month,
       size_t      pub_year)
    : _title    (std::move(title)),
      _authors  (std::move(authors)),
      _pub_day  (pub_day),
      _pub_month(std::move(pub_month)),
      _pub_year (pub_year)
     {}
 
  // ....
  // ....
};
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
std::string & toUpper(std::string & s)
{
  std::transform(s.begin(), s.end(), s.begin(), toupper);
  return s;
}
const std::string & January()
{
  static std::string jan("January");
  return jan;
}
int main(void)
{
  std::vector<std::string> authors { "A", "B", "C" };
  Book b1("Book1", authors, 1, "Jan", 2012);
 
  size_t year = 2012
  Book b2("Book2", { "A", "B", "C" }, 1, "Jan", year);
 
  std::string month = "Mar";
  Book b3("Book3", { "Author" }, 1, toUpper(month), 2012)
 
  Book b4("Book4", { "Author" }, 1, January(), 2012);
 
  std::string book = "Book";
  Book b5(std::move(book), std::move(authors), 1, std::move(month), year);
 
  Book b6("Book", { "Author" }, 1, "Jan", 2012);
}
И объяснения:
In case of b1, except for authors all other parameters are copied and moved once.
In case of b2, all strings and vectors are copied and moved once. That's what matters.
In case of b3, toUpper returns an string reference; everything else copied and moved only once.
In case of b4, January returns a const string reference; everything else copied and moved once.
In case of b5, strings are vector are moved as expected and in fact the b5 object the local parameters are created using move-constructor and moved again into the data member. Hence the object is created without any deep copies (like zero-copy).
Nick Alte
Эксперт С++
1590 / 982 / 115
Регистрация: 27.09.2009
Сообщений: 1,897
Завершенные тесты: 1
27.04.2013, 19:43     std::move, rvalue reference #4
Цитата Сообщение от Olivеr Посмотреть сообщение
В таком случае будет прямое перемещение? Не будет копирования?
Да. Разумеется, при вызове конструктора в том примере использование move тоже будет необходимо. А вот созданный прямо в месте вызова вектор можно и без move.

В приведённом примере с Book все параметры копируются. Эти копии уже и перемещаются в члены класса. Возможно, это не очень годный пример. Рассказ про zero-copy without deep copies - наглая беззастенчивая ложь.
Olivеr
 Аватар для Olivеr
411 / 407 / 13
Регистрация: 06.10.2011
Сообщений: 830
27.04.2013, 19:58  [ТС]     std::move, rvalue reference #5
Спасибо за ответ.
Кстати, по времени выполнения эта два варианты одинаковы: (0.7 сек)
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Foo
{
public:
    Foo( vector<string> v): vec( move(v) ) {}
    vector<string> vec;
};
 
int main()
{
    vector<string> s(10000000,"text-text-text!");
    cout << "vector constructed!";
    Foo f( move(s) );
    cout << boolalpha << s.empty();
    return 0;
}
и
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <utility>
#include <vector>
 
using namespace std;
 
class Foo
{
public:
    Foo( vector<string> &&v): vec( move(v) ) {}
    vector<string> vec;
};
 
int main()
{
    vector<string> s(10000000,"text-text-text!");
    cout << "vector constructed!";
    Foo f( move(s) );
    cout << boolalpha << s.empty();
    return 0;
}

Цитата Сообщение от Nick Alte Посмотреть сообщение
А вот созданный прямо в месте вызова вектор можно и без move.
Вот так?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <utility>
#include <vector>
 
using namespace std;
 
class Foo
{
public:
    Foo( vector<string> &&v): vec( v ) {} //убрал move
    vector<string> vec;
};
 
int main()
{
    vector<string> s(10000000,"text-text-text!");
    cout << "vector constructed!";
    Foo f( move(s) );
    cout << boolalpha << s.empty();
    return 0;
}
Компилятор не ругается, но время выполнения 1.4 секунды...

Если делать так:
C++
1
2
3
4
5
6
    Foo( vector<string> &&v): vec( move(v) ) {}
...
...
...
//вызов
Foo f(s); //ошибка: cannot bind 'std::vector<std::basic_string<char> >' lvalue to 'std::vector<std::basic_string<char> >&&'|
Nick Alte
Эксперт С++
1590 / 982 / 115
Регистрация: 27.09.2009
Сообщений: 1,897
Завершенные тесты: 1
28.04.2013, 09:42     std::move, rvalue reference #6
Цитата Сообщение от Olivеr Посмотреть сообщение
Вот так?
Нет, вот так:
C++
1
Foo f(vector<string> {"Foo", "bar"});
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
28.04.2013, 11:39     std::move, rvalue reference
Еще ссылки по теме:

Rvalue reference and lambda C++
Std::move C++
Error: 'move' is not a member of 'std' C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Olivеr
 Аватар для Olivеr
411 / 407 / 13
Регистрация: 06.10.2011
Сообщений: 830
28.04.2013, 11:39  [ТС]     std::move, rvalue reference #7
Цитата Сообщение от Nick Alte Посмотреть сообщение
Нет, вот так:
C++
1
Foo f(vector<string> {"Foo", "bar"});
А это понятно.
Так как vector<string> {"Foo", "bar"} является временным объектом, то не нужно убеждать компилятор (с помощью move), что это rvalue, как это было в предыдущих примерах.
Yandex
Объявления
28.04.2013, 11:39     std::move, rvalue reference
Ответ Создать тему
Опции темы

Текущее время: 04:44. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru