Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> c++ x11 и rvalue reference 
V
    Опции темы
xTr1m
Дата 25.9.2013, 17:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 692
Регистрация: 9.2.2005
Где: Москва

Репутация: 1
Всего: 1



День добрый. Просматривая стандарт, увидел такую классную штуку как rvalue reference. Захотел применить, допустим, есть код
Код

CString GetText()
{
CString str = "some big string";
return str;
}

void main()
{
CString str = GetText();
}

то есть проблема в том, что мы выделяем память память под большую строку, потом возвращаем ее из функции, но при этом вызывается конструктор копирования, который снова выделяет память и удаляет старую. Как я понимаю, тут можно использовать механику конструктора перемещения, скорее всего, используя rvalue. Но у меня не получилось. Пробовал так
Код

void main()
{
CString &&str = GetText();
}

ну и еще несколькими дурацкими способами, но не получилось. Конструктор копирования все равно вызывается. Может я что-то не так понял про r-value ref? Спасибо.
PM MAIL WWW ICQ   Вверх
vinter
Дата 25.9.2013, 18:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Explorer
****


Профиль
Группа: Завсегдатай
Сообщений: 2735
Регистрация: 1.4.2006
Где: Н.Новгород

Репутация: 13
Всего: 56



Покажите CString. У него есть конструктор CString(CString&& rhs)? Если да, то какой компилятор?

Добавлено через 1 минуту и 13 секунд
Цитата

Пробовал так

Знаете правило: никогда не брать не-константную ссылку на объект возвращенный из функции? rvalue reference в этом смысле ничем не отличается. Так что так писать весьма вредно.


--------------------
Мой блог
PM MAIL WWW   Вверх
azesmcar
Дата 25.9.2013, 18:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


Профиль
Группа: Участник Клуба
Сообщений: 6291
Регистрация: 12.11.2004
Где: Армения

Репутация: 81
Всего: 211



используй std::move

Код

#include <iostream>
#include <string>

class C
{
public:
    C()
    {
     s_ = "hello world";
    };
    C(C&& c)
    {
     s_ = std::move(c.s_);
    }
    const char *c_str() const
    {
     return s_.c_str();
    }
private:
    std::string s_;
};

int main()
{
    C c1;
    C c2 = std::move(c1);

    std::cout << c2.c_str() << std::endl;
}


Это сообщение отредактировал(а) azesmcar - 26.9.2013, 10:05
PM   Вверх
baldina
Дата 25.9.2013, 18:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



xTr1m, ничего вы таким образом не выиграете, даже наоборот. в вашем случае будет NRVO работать. 
rvalue reference хорошо применять там, где допускается семантика переноса, не очевидная компилятору (т.е. везде, где не идет речь о создании и возврате временного объекта).
PM MAIL   Вверх
xTr1m
Дата 25.9.2013, 18:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 692
Регистрация: 9.2.2005
Где: Москва

Репутация: 1
Всего: 1



По поводу правила  никогда не брать не-константную ссылку на объект возвращенный из функции это я понимаю, но как я понял rvalue как раз для этого, нет? То есть, был некий объект в функции и  мы хотим его вернуть. Понятно, что возвращать обычную ссылку нельзя, а rvalue не для этого был создан? То есть мы переносим объект из функции в другой объект без копирования, а переносом (указателя или что там еще).

CString - это из MFC и там конечно же нет такого. Делать обертку к нему? Не знаю насколько это будет целесообразно. Может посмотрю в сторону std::string, может там конструктор перемещения определен. 

За пример с std::move спасибо. Интересно, а можно использовать его с CString? Но это опять же обертку делать. А вообще как решаются такие задачи. Задача: в функции генерируется большая строка, хочу вернуть ее без перевыделения памяти.
PM MAIL WWW ICQ   Вверх
vinter
Дата 25.9.2013, 18:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Explorer
****


Профиль
Группа: Завсегдатай
Сообщений: 2735
Регистрация: 1.4.2006
Где: Н.Новгород

Репутация: 13
Всего: 56



azesmcar, убери && после C. Твой код конструктор перемещения не вызывает, а лишь создаёт rvalue ссылку.


--------------------
Мой блог
PM MAIL WWW   Вверх
azesmcar
Дата 25.9.2013, 18:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


Профиль
Группа: Участник Клуба
Сообщений: 6291
Регистрация: 12.11.2004
Где: Армения

Репутация: 81
Всего: 211



А вообще можно реализовать Copy-on-write.
PM   Вверх
xTr1m
Дата 25.9.2013, 18:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 692
Регистрация: 9.2.2005
Где: Москва

Репутация: 1
Всего: 1



Сейчас попробовал сделать так
Код

std::string GetText()
{
std::string str = "some big string";
return str;
}
void main()
{
std::string str = GetText();
}

и тут сразу вызывается конструктор перемещения. попробую засечь насколько быстрее получается.
PM MAIL WWW ICQ   Вверх
vinter
Дата 25.9.2013, 18:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Explorer
****


Профиль
Группа: Завсегдатай
Сообщений: 2735
Регистрация: 1.4.2006
Где: Н.Новгород

Репутация: 13
Всего: 56



Цитата

это я понимаю, но как я понял rvalue как раз для этого, нет? То есть, был некий объект в функции и  мы хотим его вернуть. Понятно, что возвращать обычную ссылку нельзя, а rvalue не для этого был создан?

Не совсем. Rvalue reference никогда нельзя возвращать из функции. Это моё утверждение не совсем верно, но пока полностью не проникнитесь rvalue ссылками лучше думать именно так. Т.к. возврщать ссылку на rvalue нужно в очень ограниченном числе случаев. Возвращать нужно просто по значению. Для использования семантики перемещений в возвращаемом объекте код менять не нужно, нужно просто добавить конструктор перемещения  в тот класс, перемещения которго Вы желаете.
Цитата

CString - это из MFC и там конечно же нет такого. Делать обертку к нему? Не знаю насколько это будет целесообразно.

Тем не менее это единственный выход. Другого нет.
Цитата

Может посмотрю в сторону std::string, может там конструктор перемещения определен. 

Да, все библиотечные классы имеют конструктор перемещения(для которых целесообразно, по крайней мере)

Я писал статью про rvalue references. Посмотрите, может проясните что-нибудь для себя.


--------------------
Мой блог
PM MAIL WWW   Вверх
xTr1m
Дата 25.9.2013, 18:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 692
Регистрация: 9.2.2005
Где: Москва

Репутация: 1
Всего: 1



Большое спасибо, прочту.
PM MAIL WWW ICQ   Вверх
xTr1m
Дата 25.9.2013, 19:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 692
Регистрация: 9.2.2005
Где: Москва

Репутация: 1
Всего: 1



Очень странно, написал две функции
Код

std::string GetText()
{
    std::string str("adsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsa");

    return str;
}

CString GetTextMfc()
{
    CString str("adsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsaadsa");

    return str;
}


и прогнал так
Код

for(int i=0; i<10000; ++i)
{
    std::string str = GetText();
    //CString strMfc = GetTextMfc(); // потом наоборот
}

и время выполнения в release одинаково! Хотя в одном случае идет копирование (mfc CString), а в другом перемещение (std::string). Тогда в чем выигрыш в семантике перемещения?
PM MAIL WWW ICQ   Вверх
vinter
Дата 25.9.2013, 19:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Explorer
****


Профиль
Группа: Завсегдатай
Сообщений: 2735
Регистрация: 1.4.2006
Где: Н.Новгород

Репутация: 13
Всего: 56



как выше указал(а) baldina работает NRVO, т.е. ни конструктор копирования, ни конструктор перемещения не вызываются вообще. NRVO быстрее и, соответственно, срабатывает когда можно. 
Конструктор перемещения нужен для всех остальных случаев.

А может весь код вообще выброшен компилятором как бесполезный. Всё зависит от агрессивности оптимизатора

Это сообщение отредактировал(а) vinter - 25.9.2013, 19:19


--------------------
Мой блог
PM MAIL WWW   Вверх
xTr1m
Дата 25.9.2013, 19:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 692
Регистрация: 9.2.2005
Где: Москва

Репутация: 1
Всего: 1



То есть NRVO применятеся только в релизе? (спрашиваю, так как в дебаге смотрел и вызывались конструктор копирования и перемещения)

Добавлено через 3 минуты и 16 секунд
То есть получается, что я так ничего несоптимизирую (имею в виду возврат временного объекта из функции). А можно какой-нибудь пример, где применяется семантика перемещения без временного объекта. Я пока читал про std::thread понял, что для его передачи между функциями, например, используется как раз std::move. А где еще?
PM MAIL WWW ICQ   Вверх
baldina
Дата 25.9.2013, 20:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Цитата(xTr1m @  25.9.2013,  19:33 Найти цитируемый пост)
То есть NRVO применятеся только в релизе?

да, потому что в debug отключена оптимизация

Цитата(xTr1m @  25.9.2013,  19:33 Найти цитируемый пост)
какой-нибудь пример

Код

class String {
  char *buf;
  size_t size;

  public:
     ...

     String (String&& o) : buf(std::move(o.buf), size(std::move(o.size) {}

     ~String () { delete buf; }
};


Это сообщение отредактировал(а) baldina - 25.9.2013, 20:22
PM MAIL   Вверх
vinter
Дата 25.9.2013, 20:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Explorer
****


Профиль
Группа: Завсегдатай
Сообщений: 2735
Регистрация: 1.4.2006
Где: Н.Новгород

Репутация: 13
Всего: 56



Цитата

То есть NRVO применятеся только в релизе?

NRVO это дело оптимизатора. Оптимизация, как правило, в дебаге выключена. RVO и NRVO не всегда применимы. Тогда идёт выигрыш от перемещения. 
std::unique_ptr еще без перемещения не работает. Применяется везде где нужно, где есть объект который можно переместить и там где есть объект который только и можно перемещать.
Многие отказываются от передачи по константной ссылке аргументов функции в пользу аргументов по значению, когда передаются типы с перемещающим конструктором. Вариантов масса. Просто искусственно не нужно использовать, я считаю. Ну и взять в привычку добавлять конструктор перемещения для всех своих классов у которых есть явный конструктор копирования.

Это сообщение отредактировал(а) vinter - 25.9.2013, 20:16


--------------------
Мой блог
PM MAIL WWW   Вверх
baldina
Дата 25.9.2013, 20:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



кстати, можно просто
Код

String (String&& other) = default;



Это сообщение отредактировал(а) baldina - 25.9.2013, 20:23
PM MAIL   Вверх
vinter
Дата 25.9.2013, 20:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Explorer
****


Профиль
Группа: Завсегдатай
Сообщений: 2735
Регистрация: 1.4.2006
Где: Н.Новгород

Репутация: 13
Всего: 56



baldina, нельзя, он у тебя просто буфер скопирует побитово и, в результате, будет утечка.
Да и std::move в твоём ответе лишние(избыточные) явно.

Это сообщение отредактировал(а) vinter - 25.9.2013, 20:24


--------------------
Мой блог
PM MAIL WWW   Вверх
baldina
Дата 25.9.2013, 20:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



тут посмотри http://en.cppreference.com/w/cpp/language/move_constructor

Добавлено через 44 секунды
vinter, он побитово скопирует указатель. где утечка?
PM MAIL   Вверх
vinter
Дата 25.9.2013, 20:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Explorer
****


Профиль
Группа: Завсегдатай
Сообщений: 2735
Регистрация: 1.4.2006
Где: Н.Новгород

Репутация: 13
Всего: 56



предыдущий указатель не удалится. А новый, кстати, будет удалён xvalue(тот с которого переместили внутренности) объектом. Так что не просто утечка, а утечка с невалидным указателем в новом объекте. Первый твой ответ был правильным.

Добавлено через 5 минут и 31 секунду
Про утечку я неправильно сказал, это же конструктор. Но вопрос с невалидным указателем остается.

Это сообщение отредактировал(а) vinter - 25.9.2013, 20:30


--------------------
Мой блог
PM MAIL WWW   Вверх
xTr1m
Дата 25.9.2013, 20:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 692
Регистрация: 9.2.2005
Где: Москва

Репутация: 1
Всего: 1



Всем большое спасибо. Пойду еще статью прочитаю, чтобы точно все понять =)) А пока понял, что не нужно применять что бы просто применить (пусть и очень хочется =)) То есть использование достаточно специфическое, и в повседневном коде можно обойтись стандартными инструментами. 
PM MAIL WWW ICQ   Вверх
akizelokro
Дата 25.9.2013, 20:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Крокодил
**


Профиль
Группа: Участник
Сообщений: 761
Регистрация: 30.7.2007

Репутация: 1
Всего: 5



Цитата(xTr1m @  25.9.2013,  17:44 Найти цитируемый пост)
 Пробовал так

А так не пробовал?

Код

const CString & cs = GetText();


Добавлено @ 20:44
Да можно применять.  Если с CString прокатит, конечно.

Это сообщение отредактировал(а) akizelokro - 25.9.2013, 20:52


--------------------
a = a + b; b = a - b; a = a - b;
PM MAIL   Вверх
xTr1m
Дата 25.9.2013, 20:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 692
Регистрация: 9.2.2005
Где: Москва

Репутация: 1
Всего: 1



Сейчас попробовал. По времени также, как если бы без константной ссылки
PM MAIL WWW ICQ   Вверх
baldina
Дата 25.9.2013, 21:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Цитата(vinter @  25.9.2013,  20:26 Найти цитируемый пост)
вопрос с невалидным указателем остается

почему он невалидный?
если не использовать явный перенос (т.е. конструктор применяется только к временным объектам), то все в порядке
http://ideone.com/DkFQbw

если использовать явный перенос, надо предотвратить повторное освобождение памяти в деструкторе
http://ideone.com/D4m7KI
ты об этом?
PM MAIL   Вверх
akizelokro
Дата 25.9.2013, 21:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Крокодил
**


Профиль
Группа: Участник
Сообщений: 761
Регистрация: 30.7.2007

Репутация: 1
Всего: 5



Цитата(xTr1m @  25.9.2013,  20:46 Найти цитируемый пост)
Сейчас попробовал. По времени также, как если бы без константной ссылки 


Да не должно меняться при присваивании константной ссылке. В этом случае эффект, что время жизни созданного временного обьекта будет равно времени жизни константной ссылки. Кажись, единственный эффект.
Если не жаль времени, то попробуй такой вариант

Код

string& GetText()
{
  return (string("rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr"));
}

const string & cs = GetText();




Это сообщение отредактировал(а) akizelokro - 25.9.2013, 21:29


--------------------
a = a + b; b = a - b; a = a - b;
PM MAIL   Вверх
vinter
Дата 26.9.2013, 08:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Explorer
****


Профиль
Группа: Завсегдатай
Сообщений: 2735
Регистрация: 1.4.2006
Где: Н.Новгород

Репутация: 13
Всего: 56



baldina, 
Цитата

почему он невалидный?
если не использовать явный перенос (т.е. конструктор применяется только к временным объектам), то все в порядке

Там не всё в порядке. Ты в этом коде так и не вызвал конструктор перемещения. Добавь в тот код еще одну строчку(String c(std::move(a));) и ты увидишь, что указатель удалится 2 раза. Что уже является UB. Но в общем случае до второго удаления есть еще пользование этим объектом, у которого, на деле, указатель уже невалиден.
Мне кажется ты не совсем понимаешь всю это чехарду с перемещением. std::move и конструктор перемещения никогда и ничего не перемещают, если только ты не делаешь этого явно. Чем, по сути, отличается конструктор копирования от конструктора перемещения по умолчанию? первый делает a = rhs.a, а второй a = std::move(rhs.a);. Для композитных объектов  класса у которых нет конструктора перемещения поведение обоих конструкторов по умолчанию идентично. Т.е. в твоём случае это наличие двух объектов, которые ссылаются на один буфер, что есть заезженная проблема из старого доброго C++ к которой прибегают всегда, когда объясняют зачем писать пользовательский конструктор копирования.
Замени char* на std::string и всё будет работать с конструктором перемещения по умолчанию, т.к. std::string "знает" как работать с перемещением.
Твой второй пример есть пример правильного написания конструктора перемещения.

Добавлено через 3 минуты и 34 секунды
akizelokro, так писать нельзя, ты возвращаешь ссылку на объект, которого больше нет.
Должно быть так:
Код

string GetText()
{
  return (string("rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr"));
}
const string & cs = GetText();

В этом случае время жизни временного объекта продлевается на всё время жизни константной ссылки. Это справедливо только для константных ссылок.


--------------------
Мой блог
PM MAIL WWW   Вверх
baldina
Дата 26.9.2013, 08:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



vinter, так я об этом и написал, только букф поменьше...
PM MAIL   Вверх
vinter
Дата 26.9.2013, 08:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Explorer
****


Профиль
Группа: Завсегдатай
Сообщений: 2735
Регистрация: 1.4.2006
Где: Н.Новгород

Репутация: 13
Всего: 56



Да нет же. Твой код с String (String&&) = default; неверен. И я объяснил почему.


--------------------
Мой блог
PM MAIL WWW   Вверх
azesmcar
Дата 26.9.2013, 10:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


Профиль
Группа: Участник Клуба
Сообщений: 6291
Регистрация: 12.11.2004
Где: Армения

Репутация: 81
Всего: 211



Цитата(vinter @  25.9.2013,  18:25 Найти цитируемый пост)
azesmcar, убери && после C. Твой код конструктор перемещения не вызывает, а лишь создаёт rvalue ссылку. 

ага, спасибо.
PM   Вверх
Страницы: (2) [Все] 1 2 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0977 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.