Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.56/9: Рейтинг темы: голосов - 9, средняя оценка - 4.56
413 / 409 / 95
Регистрация: 06.10.2011
Сообщений: 832
1

std::move, rvalue reference

27.04.2013, 18:57. Просмотров 1857. Ответов 6
Метки нет (Все метки)

Здравствуйте! Недавно начал разбираться с новыми способами передачи аргументов. Прочитал около 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
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
27.04.2013, 18:57
Ответы с готовыми решениями:

C++ expressions - rvalue, glvalue, prvalue, xvalue, lvalue, а также rvalue reference: что есть что?
Доброго времени суток, не понимаю до конца деление С++ - выражений (приложение 1). Lvalue вроде...

[C++11] move\rvalue ref semantic
Перегрузил оператор присвоения используя move\rvalue ref semantic. /** * Copy operator ...

RVALUE Ссылка, error: cannot bind non-const lvalue reference of type 'String&' to an rvalue of type 'String'|
Код не компилируется ниже 17 стандарта с++ с ошибкой error: cannot bind non-const lvalue reference...

Rvalue reference
Добрый вечер! Помогите пожалуйста разобраться с rvalue reference: template &lt;class T&gt; void...

6
Эксперт С++
1659 / 1031 / 174
Регистрация: 27.09.2009
Сообщений: 1,945
27.04.2013, 19:14 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;
};
1
413 / 409 / 95
Регистрация: 06.10.2011
Сообщений: 832
27.04.2013, 19:25  [ТС] 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).
0
Эксперт С++
1659 / 1031 / 174
Регистрация: 27.09.2009
Сообщений: 1,945
27.04.2013, 19:43 4
Цитата Сообщение от Olivеr Посмотреть сообщение
В таком случае будет прямое перемещение? Не будет копирования?
Да. Разумеется, при вызове конструктора в том примере использование move тоже будет необходимо. А вот созданный прямо в месте вызова вектор можно и без move.

В приведённом примере с Book все параметры копируются. Эти копии уже и перемещаются в члены класса. Возможно, это не очень годный пример. Рассказ про zero-copy without deep copies - наглая беззастенчивая ложь.
1
413 / 409 / 95
Регистрация: 06.10.2011
Сообщений: 832
27.04.2013, 19:58  [ТС] 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> >&&'|
0
Эксперт С++
1659 / 1031 / 174
Регистрация: 27.09.2009
Сообщений: 1,945
28.04.2013, 09:42 6
Цитата Сообщение от Olivеr Посмотреть сообщение
Вот так?
Нет, вот так:
C++
1
Foo f(vector<string> {"Foo", "bar"});
1
413 / 409 / 95
Регистрация: 06.10.2011
Сообщений: 832
28.04.2013, 11:39  [ТС] 7
Цитата Сообщение от Nick Alte Посмотреть сообщение
Нет, вот так:
C++
1
Foo f(vector<string> {"Foo", "bar"});
А это понятно.
Так как vector<string> {"Foo", "bar"} является временным объектом, то не нужно убеждать компилятор (с помощью move), что это rvalue, как это было в предыдущих примерах.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
28.04.2013, 11:39

Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь.

Rvalue reference
#include &lt;iostream&gt; std::string get_string() { return std::string(&quot;12345&quot;); } int main()...

Rvalue reference and lambda
void foo(A&amp;&amp; a) { auto l = () {}; //a? } Как передать в лямбду rvalue ref как просто...

Возвращаемый тип как rvalue reference
Нашел интересный пример в книге Мейерса Эффективный и современный С++. class Widget { ...

Rvalue reference. Что происходит в коде?
В консоли пусто. Но &quot;worked&quot; выводит. Почему? #include &lt;iostream&gt; using std::cout; class...


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

Или воспользуйтесь поиском по форуму:
7
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2020, vBulletin Solutions, Inc.