Russian Qt Forum

Программирование => С/C++ => Тема начата: ssoft от Ноябрь 22, 2018, 10:54



Название: rvalue и operator ->
Отправлено: ssoft от Ноябрь 22, 2018, 10:54
Привет всем!

Имеется возможность реализовывать методы конкретно для rvalue и lvalue значений, например,

Код
C++ (Qt)
struct My
{
   void method () && {}
   void method () & {}
};
 

таким образом закладывать для rvalue экземпляров типа My более эффективную реализацию, или даже предусматривать разный api

Код
C++ (Qt)
struct My
{
   void rvalueMethod () && {}
   void lvalueMethod () & {}
};
 

Однако использование поверх типа My любого указателя (или обертки) с доступом к членам через оператор -> не позволяет вызвать rvalue методы.
Например, для указателей std

Код
C++ (Qt)
::std::make_shared< My >()->lvalueMethod(); // OK
::std::make_shared< My >()->rvalueMethod(); // Error
 

На просторах интернета нашел только http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3723.html

Можно ли вообще реализовать вызов rvalue метода посредством оператора -> ?


Название: Re: rvalue и operator ->
Отправлено: ViTech от Ноябрь 22, 2018, 12:19
Если решать "в лоб", то как-то так получается:
Код
C++ (Qt)
#include <memory>
#include <iostream>
 
using namespace std;
 
struct A
{
   void lvalueMethod() &
   { cout << "lvalueMethod" << endl; }
 
   void rvalueMethod() &&
   { cout << "rvalueMethod" << endl; }
};
 
int main()
{
   make_shared<A>()->lvalueMethod();
   move(*make_shared<A>().operator->()).rvalueMethod();
 
   return 0;
}

А так может надо мутить с переопределением operator->(), чтобы возвращалась lvalue или rvalue в зависимости от того, применяется этот оператор к lvalue или rvalue. Но это в своих классах так можно, от стандартных, типа shared_ptr такого вряд ли дождёшься.


Название: Re: rvalue и operator ->
Отправлено: Авварон от Ноябрь 22, 2018, 16:52
Достаточно очевидно, что у _шаред_ птра не может быть рвалуе оператора. Надо объяснять почему?


Название: Re: rvalue и operator ->
Отправлено: ssoft от Ноябрь 22, 2018, 17:22
Достаточно очевидно, что у _шаред_ птра не может быть рвалуе оператора. Надо объяснять почему?

Так и знал, что конкретно на shared_ptr обратят внимание  ;D ;D ;D
Он здесь так сбоку, просто для примера.

А если я такой специфический класс-обертку хочу реализовать?

Код
C++ (Qt)
template < typename _Value >
class Wraper
{
   _Value m_value;
public:
   _Value * operator -> () & { return &m_value; }
   // ??? operator -> () && { return ???; }
};
 

Но чтобы работало так

Код
C++ (Qt)
   Wraper< My >()->rvalueMethod();
 

Возможно такое реализовать в принципе?
Или ждать возможного внедрения предложения в стандарт N3723 (ссылка вначале) или предложений для оператора "точка" (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4477.pdf (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4477.pdf) , http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0352r1.pdf (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0352r1.pdf)?


Название: Re: rvalue и operator ->
Отправлено: ssoft от Ноябрь 22, 2018, 17:34
А так может надо мутить с переопределением operator->(), чтобы возвращалась lvalue или rvalue в зависимости от того, применяется этот оператор к lvalue или rvalue. Но это в своих классах так можно, от стандартных, типа shared_ptr такого вряд ли дождёшься.

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

Код
C++ (Qt)
int * a = &3; // Error
 

Но и не нужен сам указатель, нужен просто доступ к rvalueMethod() без всяких cast и move.


Название: Re: rvalue и operator ->
Отправлено: Авварон от Ноябрь 22, 2018, 18:18
А, я тупанул, да, там же указатель.
Как насчет попробовать сделать аналог std::reference_wrapper?


Название: Re: rvalue и operator ->
Отправлено: ssoft от Ноябрь 23, 2018, 08:01
А, я тупанул, да, там же указатель.
Как насчет попробовать сделать аналог std::reference_wrapper?

Собственно, и делаю такую библиотечку, куда входит набор враперов.
Хотел добиться абсолютной прозрачности API для  враперов, но похоже есть пока непреодолимые языковые ограничения.
В принципе, не очень критично, так как случай крайне редкий. Тем не менее - это является ограничением возможности их использования.


Название: Re: rvalue и operator ->
Отправлено: Igors от Ноябрь 23, 2018, 10:40
Имеется возможность реализовывать методы конкретно для rvalue и lvalue значений, например,

Код
C++ (Qt)
struct My
{
   void method () && {}
   void method () & {}
};
 

таким образом закладывать для rvalue экземпляров типа My более эффективную реализацию, или даже предусматривать разный api
Если я правильно понял, то "методы для lvalue" - это как сейчас/всегда, но что за "для rvalue" - без понятия. Что такое rvalue (и почему не имеет адреса) понимаю, но методы здесь причем? Можно больше пояснений/примерчик? Спасибо 


Название: Re: rvalue и operator ->
Отправлено: ssoft от Ноябрь 23, 2018, 11:31
Если я правильно понял, то "методы для lvalue" - это как сейчас/всегда, но что за "для rvalue" - без понятия. Что такое rvalue (и почему не имеет адреса) понимаю, но методы здесь причем? Можно больше пояснений/примерчик? Спасибо 

Вообще с стандарте C++  целый раздел есть по rvalue и вот неплохой материал для изучения
https://habr.com/post/322132/

Методы без уточнения &/&& одинаковы и для lvalue и rvalue значений.
К rvalue значениям относятся в том числе временные объекты, время жизни которых ограничено вызовами методов.
Факт того, что время жизни временного объекта скоротечно), может быть использован для реализации более эффективного кода (без лишних копирований, аллокаций и т.п.).
Предусмотрена возможность явного преобразования lvalue ссылки к rvalue с помощью ::std::move. В этом случае контроль времени жизни ложится на хрупкие плечи программиста).


Название: Re: rvalue и operator ->
Отправлено: ViTech от Ноябрь 23, 2018, 11:55
Так вот, что бы такое намутить в своих классах?
Проблема в том, что оператор -> должен возвращать указатель, т.е. конкретный адрес памяти.

Да, хоть operator-> и может возвращать промежуточные объекты, получается, что в конечном итоге на выходе всё равно подавай указатель, к содержимому которого идёт обращение как к lvalue. У меня тоже не получилось через operator-> добраться до rvalueMethod().

Или ждать возможного внедрения предложения в стандарт N3723 (ссылка вначале) или предложений для оператора "точка" (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4477.pdf (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4477.pdf) , http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0352r1.pdf (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0352r1.pdf)?

Надеяться, конечно, можно, но особых подвижек по operator dot что-то не замечаю. Может слишком долго ждать придётся :).


Название: Re: rvalue и operator ->
Отправлено: Igors от Ноябрь 23, 2018, 13:56
Вообще с стандарте C++  целый раздел есть по rvalue и вот неплохой материал для изучения
https://habr.com/post/322132/
Ой :) Может не стоит так-то, "через край" (все хорошо в меру, и изучение тонкостей языка тоже). А освободившееся время посвятить предметной части, не раз замечал что это гораздо эффективнее.


Название: Re: rvalue и operator ->
Отправлено: ssoft от Ноябрь 23, 2018, 23:55
Ой :) Может не стоит так-то, "через край" (все хорошо в меру, и изучение тонкостей языка тоже). А освободившееся время посвятить предметной части, не раз замечал что это гораздо эффективнее.

Так это ж теперь одна из самых важных основ языка  :o, особенно если пишется высокопроизводительный код  ;D ;D ;D.
Предметная часть сама по себе важна, но не менее важно наличие простых, удобных и эффективных инструментов для преобразования предметной части в программное обеспечение.
А как только дело касается инструментов, то тонкости языка сами становятся предметной частью ;)


Название: Re: rvalue и operator ->
Отправлено: Igors от Ноябрь 24, 2018, 07:59
Так это ж теперь одна из самых важных основ языка  :o, особенно если пишется высокопроизводительный код  ;D ;D ;D.
Преувеличение. Просто не хватает терпения, аккуратности. Сначала бузует все по значению ( QString ), а потом начинает "мувать". Как известно, чисто не там где убирают...

Статью "ни асилил"  :)
Цитировать
std::string &&str = std::string("Hello");
std::string *psrt = &str;

Выясняется, что да, можно, так как str — lvalue, хотя его тип rvalue-ссылка. Запомните, это важно.
Ни фига не понял, что "физически" произошло? Кто (или где) сохранил строку "Hello"? Поясните. Спасибо


Название: Re: rvalue и operator ->
Отправлено: ViTech от Ноябрь 24, 2018, 12:09
Ни фига не понял, что "физически" произошло? Кто (или где) сохранил строку "Hello"? Поясните. Спасибо

Тут такая история. Что именно произошло - точно не известно. Давеча видел эту строку в тёмном переулке, что по адресу 0x7fffffffdf30. В угол забилась, string_view прикрылась, не шелохнется... авось никто не заметит.Времена-то после 11-го года суровые пошли. Могут кого угодно куда угодно переместить, да ещё и все внутренности при этом вынуть. Представляете?! Я мимо прошёл, сделал вид, что не заметил. И вы, пожалуйста, тоже про этот адрес больше никому не говорите! Строчку жалко  :'(...


Название: Re: rvalue и operator ->
Отправлено: ssoft от Ноябрь 25, 2018, 22:06
Ни фига не понял, что "физически" произошло? Кто (или где) сохранил строку "Hello"? Поясните. Спасибо

Все что объявляется явно в виде переменной является lvalue значением указанного типа, у которого имеется конкретный адрес.
Сам тип значения может быть T (value), T& (lvalue) или T&& (rvalue). Ссылки тоже являются полноценными типами.
Для инициализации ссылочных типов с помощью временных объектов, стандартом оговорен механизм "продления жизни" возвращаемых значений.
Каким образом технически работает механизм "продления жизни" скрыто в недрах компилятора (может быть разным для разных компиляторов).
Таким образом в примере представлен случай формирования экземпляра lvalue, типа rvalue ссылки на экземпляр значения с "продленной жизнью", возвращенный функцией std::string("Hello").


Название: Re: rvalue и operator ->
Отправлено: Igors от Ноябрь 26, 2018, 06:16
Все что объявляется явно в виде переменной является lvalue значением указанного типа, у которого имеется конкретный адрес.
Сам тип значения может быть T (value), T& (lvalue) или T&& (rvalue). Ссылки тоже являются полноценными типами.
Наверное "полноценными переменными". Ну ладно, начнем с того что все знают
Код
C++ (Qt)
QList<int> lst = this->GeList();
 
Никакого копирования здесь не произойдет (ну разве что у MSVC в Debug), фактически будет создан такой код
Код
C++ (Qt)
QList<int> lst;
this->GeList(lst);
 
Как если бы GetList не возвращал котейнер, а принимал его по ссылке/указателю.
Код
C++ (Qt)
std::string &&str = std::string("Hello");
 
А тут, стало быть, то же самое, компилятор как-то избегает копирования (как - его личное дело), но возвращает модную && ссылку. Верно ли я понял?

Для инициализации ссылочных типов с помощью временных объектов, стандартом оговорен механизм "продления жизни" возвращаемых значений.
Каким образом технически работает механизм "продления жизни" скрыто в недрах компилятора (может быть разным для разных компиляторов).
Таким образом в примере представлен случай формирования экземпляра lvalue, типа rvalue ссылки на экземпляр значения с "продленной жизнью", возвращенный функцией std::string("Hello").
А разве тут есть выбор? Какие еще могут быть варианты кроме как ссылка str && выйдет из области видимости?

Ну ладно, а (оптимальные) методы здесь каким боком?


Название: Re: rvalue и operator ->
Отправлено: ssoft от Ноябрь 26, 2018, 08:23
Код
C++ (Qt)
QList<int> lst = this->GeList();
 
Никакого копирования здесь не произойдет (ну разве что у MSVC в Debug) ...

Это относится к оптимизации конкретного компилятора. По факту должен быть вызван конструктор копирования (до C++11) или конструктор перемещения (начиная с C++11).
Умный компилятор производит здесь оптимизацию (которую можно отключить), чтобы лишний раз не вызывать конструктор.
Это не относится к стандарту языка, это личное дело каждого компилятора.

Код
C++ (Qt)
std::string &&str = std::string("Hello");
 
А тут, стало быть, то же самое, компилятор как-то избегает копирования (как - его личное дело), но возвращает модную && ссылку. Верно ли я понял?
А разве тут есть выбор? Какие еще могут быть варианты кроме как ссылка str && выйдет из области видимости?
Ну ладно, а (оптимальные) методы здесь каким боком?

Этот случай явно прописан в стандарте языка (подробнее https://en.cppreference.com/w/cpp/language/reference_initialization#Lifetime_of_a_temporary).
Временному объекту "продляют жизнь" если на него имеется ссылка. Областью видимости такого временного объекта является область видимости вызова метода.


Название: Re: rvalue и operator ->
Отправлено: Авварон от Ноябрь 26, 2018, 12:18

Это относится к оптимизации конкретного компилятора. По факту должен быть вызван конструктор копирования (до C++11) или конструктор перемещения (начиная с C++11).
Умный компилятор производит здесь оптимизацию (которую можно отключить), чтобы лишний раз не вызывать конструктор.
Это не относится к стандарту языка, это личное дело каждого компилятора.

Вообще-то 11й стандарт говорит о том, когда применяется RVO и copy-elision.


Название: Re: rvalue и operator ->
Отправлено: ViTech от Ноябрь 26, 2018, 12:34
Это относится к оптимизации конкретного компилятора. По факту должен быть вызван конструктор копирования (до C++11) или конструктор перемещения (начиная с C++11).
Умный компилятор производит здесь оптимизацию (которую можно отключить), чтобы лишний раз не вызывать конструктор.
Это не относится к стандарту языка, это личное дело каждого компилятора.

К стандарту С++17 относится: Guaranteed Copy Elision (https://jonasdevlieghere.com/guaranteed-copy-elision/). Осталось только, чтобы компиляторы полно и точно соответствовали стандартам :).


Название: Re: rvalue и operator ->
Отправлено: _Bers от Декабрь 13, 2018, 16:41
А если я такой специфический класс-обертку хочу реализовать?

Код
C++ (Qt)
template < typename _Value >
class Wraper
{
   _Value m_value;
public:
   _Value * operator -> () & { return &m_value; }
   // ??? operator -> () && { return ???; }
};
 

Но чтобы работало так

Код
C++ (Qt)
   Wraper< My >()->rvalueMethod();
 

Возможно такое реализовать в принципе?


https://rextester.com/FAQKW17630

Код:
#include <iostream>

template < typename value >
class Wraper
{
    // _Value m_value;  <-- UB
    value m_value;
public:
    Wraper():m_value(10){}
   
    value* lvalueMethod() &  { return &m_value; }
    value* rvalueMethod() && { return &m_value; }
};

int main()
{
    auto r = *Wraper<int>().rvalueMethod();
    std::cout << r << '\n';
}






Название: Re: rvalue и operator ->
Отправлено: ViTech от Декабрь 13, 2018, 17:01
Код:
#include <iostream>

template < typename value >
class Wraper
{
    // _Value m_value;  <-- UB
    value m_value;
public:
    Wraper():m_value(10){}
   
    value* lvalueMethod() &  { return &m_value; }
    value* rvalueMethod() && { return &m_value; }
};

int main()
{
    auto r = *Wraper<int>().rvalueMethod();
    std::cout << r << '\n';
}


А где здесь вызов operator->?


Название: Re: rvalue и operator ->
Отправлено: ssoft от Декабрь 14, 2018, 08:19


https://rextester.com/FAQKW17630

Код
C++ (Qt)
#include <iostream>
 
template < typename value >
class Wraper
{
   // _Value m_value;  <-- UB
   value m_value;
public:
   Wraper():m_value(10){}
 
   value* lvalueMethod() &  { return &m_value; }
   value* rvalueMethod() && { return &m_value; }
};
 
int main()
{
   auto r = *Wraper<int>().rvalueMethod();
   std::cout << r << '\n';
}
 


Wrapper не должен иметь интерфейса. rvalueMethod() является методом для типа value, а не Wrapper.

Код
C++ (Qt)
struct My
{
   void rvalueMethod () && {}
   void lvalueMethod () & {}
};
 
template < typename _Value >
class Wraper
{
   _Value m_value;
public:
   _Value * operator -> () & { return &m_value; }
   // ??? operator -> () && { return ???; }
};
 
void foo ()
{
   Wraper< My >()->rvalueMethod();
}
 


Название: Re: rvalue и operator ->
Отправлено: _Bers от Декабрь 14, 2018, 12:38
Wrapper не должен иметь интерфейса. rvalueMethod() является методом для типа value, а не Wrapper.

так ведь категории доступа к объекту (rvalue/lvalue) имеют смысл
только в контексте действий с самим объектом.

указатель же хранит адрес объекта.
но он не хранит информации о том, временный или нет этот объект.

если даже сам указатель временный, это не значит,
что он указывает на объект, который тоже временный.
и вообще - не известно, что там с этим объектом.


вот когда изобретут синтаксис по аналогии с квалификатором const:
Код:
// rvalue-reference не изменяемого указателя, который хранит адрес временного не изменяемого объекта
const pointer &&* && pObject const = get();

но зачем нужно срисовывать указатель на временный объект?
какой в этом смысл?

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

нафига нужен такой адрес?


ТС нужно понять, чего он на самом деле хочет:
получать доступ к объекту-ресурсу враппера,
или зачем то хранить его адрес?


если ТС хочет получать ништяки от доступа к временному ресурсу
(который временный, потому что сам враппер временный),
то ему нужно работать именно с этим временным ресурсом,
а не с указателем на него.

Код:
#include <iostream>
#include <memory>
#include <string>

struct payload
{
    std::string m_value = "hello";

    std::string& lvalueMethod()&  { return m_value;            }
    std::string&& rvalueMethod()&& { return std::move(m_value); }
};

template<typename value> struct wrapper
{
    value m_value = {};
   
    value&  operator*()&  { return m_value; }
    value&& operator*()&& { return std::move(m_value); }
};

int main()
{
    std::string&& val = (*wrapper<payload>()).rvalueMethod(); // OK

    std::cout << "val = " << std::endl;
}


Название: Re: rvalue и operator ->
Отправлено: ssoft от Декабрь 14, 2018, 16:17
ТС хочет получать ништяки от доступа к временному ресурсу
(который временный, потому что сам враппер временный).

Да именно так. Но не хотелось бы использовать дополнительные разыменования и др. методы и операторы.
Оператор '->' мог бы помочь, но не может), так как требует в конечном счете адрес, которого у rvalue нет.

Замечу (на всякий случай, не для холивара), что ::std::move использовать здесь не уместно, должен быть ::std::forward.
А то можно и накосячить с типами случайно.

Код
C++ (Qt)
#include <iostream>
#include <memory>
#include <string>
 
struct payload
{
   std::string m_value = "hello";
 
   std::string& lvalueMethod()&  { return m_value;            }
   std::string&& rvalueMethod()&& { return std::forward< std::string&& >(m_value); }
};
 
template<typename value> struct wrapper
{
   value m_value = {};
 
   value&  operator*()&  { return m_value; }
   value&& operator*()&& { return std::forward< value&& >(m_value); }
};
 
int main()
{
   std::string&& val = (*wrapper<payload>()).rvalueMethod(); // OK
 
   std::cout << "val = " << std::endl;
}
 


Название: Re: rvalue и operator ->
Отправлено: Авварон от Декабрь 14, 2018, 16:53
Ну, вообще, какой-нибудь std::optional эмулирует указатель, хотя является объектом "на стеке". В целом, для него полезно было бы получать временный "указатель":
Код:
std::optional<T> getBar();
auto foo = getBar()->rvalueMathod();

С другой стороны, optional надо проверять на bool, что сложно сделать без временной переменной...


Название: Re: rvalue и operator ->
Отправлено: _Bers от Декабрь 21, 2018, 17:26
Да именно так. Но не хотелось бы использовать дополнительные разыменования и др. методы и операторы.

если по смыслу предполагается работать с объектом,
то и нужно работать именно с объектом.
предоставлять клиентам адрес объекта вместо самого объекта тогда,
когда нужен именно объект - это какой то бред не вполне логично.
вы не находите свои хотелки слегка странными?

Оператор '->' мог бы помочь, но не может), так как требует в конечном счете адрес, которого у rvalue нет.

rvalue - категория доступа к объекту.
(это не существительное. это - прилагательное)
у объекта всегда есть адрес.

Замечу (на всякий случай, не для холивара), что ::std::move использовать здесь не уместно, должен быть ::std::forward.
А то можно и накосячить с типами случайно.

не очевидно, почему не уместно, и как именно можно накосячить.

но поскольку очевидно, что std::move - функция,
задача которой в том и заключается,
что бы обозначить объект как rvalue,
то совершенно логично именно её и использовать,
по её прямому назначению.


что касается шаблонной std::forward,
её назчачение - разруливать возможные ситуации
с так называемыми 'forwarding reference':
передача forwarding reference аргумента в другие функции.

отсюда и область применения:
только в шаблонах функций,
аргументами которых являются 'универсальные ссылки'
(так Скотт Майерс обозвал 'forwarding reference'. погремушка прижилась)

применение конструкции вне шаблонов не имеет особого практического смысла.
конечно, инстанцируся явным образом под rvalue-reference параметр,
её можно использовать в качестве альтернативы std::move,
вот только зачем зазря усложнять синтаксис,
используя вещь не по назначению?



Название: Re: rvalue и operator ->
Отправлено: ssoft от Декабрь 21, 2018, 22:33
предоставлять клиентам адрес объекта вместо самого объекта тогда,
когда нужен именно объект - это какой то бред не вполне логично.
вы не находите свои хотелки слегка странными?
Я, нет).
Реализую обертку, которая может притворятся агрегированным значением, но с добавлением дополнительных особенностей.
Например, copy on write, thread safe и т.п. Для пользователя такой объект выглядит, как обычный пользовательский тип.
Интерфейс обертки полностью "прозрачен", кроме двух исключений:
- доступ к членам класса агрегированного значения осуществляется через оператор '->', вместо оператора '.';
- rvalue значение обертки не позволяет вызвать rvalueMethod с помощью оператора '->'.

В остальном всё получилось.

Доступ к rvalueMethod осуществляется почти так же, как описано выше:

Код
C++ (Qt)
(*&wrapper<payload>()).rvalueMethod();
 

оператор '&' возвращает временное значение, наделяющее payload дополнительными особенностями, а оператор '*' уже ссылку нужного типа на сам payload.

Таким образом, можно наделить, например, контейнеры stl свойством copy-on-write, или thread-safe, или  и тем и другим (в примере Instance - та самая обертка)

Код
C++ (Qt)
// Потокобезопасный неявно распределенный вектор целочисленных значений
using ThreadSafeImplisitVector = Instance< Instance< ::std::vector< int >, Implicit >, ThreadSafe >;
ThreadSafeImplisitVector featured_vector;
// применять обычным способом
for ( int i =0; i < 100; ++i )
   featured_vector[i] = i;
 

но поскольку очевидно, что std::move - функция,
задача которой в том и заключается,
что бы обозначить объект как rvalue
...
что касается шаблонной std::forward,
её назчачение - разруливать возможные ситуации
с так называемыми 'forwarding reference':
передача forwarding reference аргумента в другие функции.

Это правильно, forward отличается от move именно этим.

Код
C++ (Qt)
template < typename _Arg >
decltype(auto) foo ( _Arg && arg )
{
   return ::std::move( arg );
}
 
template < typename _Arg >
decltype(auto) bar ( _Arg && arg )
{
   return ::std::forward< _Arg >( arg );
}
 

будет иметь совсем разный эффект

Код
C++ (Qt)
Data value;
foo( value ); // rvalue
bar( value ); // lvalue
 
foo( Data() ); // rvalue
bar( Data() ); // rvalue
 

поэтому move следует применять явно над значением объекта, который необходимо переместить, а forward для последующей работы с с этим значением далее.


Название: Re: rvalue и operator ->
Отправлено: ssoft от Декабрь 21, 2018, 22:37
применение конструкции вне шаблонов не имеет особого практического смысла.

И это верно). Но в данном случае как раз и  используется шаблон.

Код
C++ (Qt)
template<typename value> struct wrapper;
 



Название: Re: rvalue и operator ->
Отправлено: _Bers от Декабрь 24, 2018, 14:01
Я, нет).

есть такая фраза: пьяный проспиццо, дурак - никогда(ц) Петр I

Реализую обертку, которая может притворятся агрегированным значением,

твоя обертка со своим оператором 'стрелочка' косит под указатель.
не удивительно, что ты столкнулся с специфичным для указателей поведением.

знаешь, чем указатель отличается от объекта?
или от ссылки на объект?

поэтому move следует применять явно над значением объекта, который необходимо переместить, а forward для последующей работы с с этим значением далее.
почему же ты предложил использовать forward не по назначению?




Название: Re: rvalue и operator ->
Отправлено: _Bers от Декабрь 24, 2018, 14:04
применение конструкции вне шаблонов не имеет особого практического смысла.

И это верно). Но в данном случае как раз и  используется шаблон.

Код
C++ (Qt)
template<typename value> struct wrapper;
 



нет, не верно.
не нужно путать понятие "шаблон класса" и "шаблон функции".

обычные функции-члены шаблонна-класса
сами по себе шаблонными не являются.

ты за каким то неведомым фигом захотел использовать std::forward в не_шаблонной функции.



вот пример грамотного использования std::forward:

Код:
#include <iostream>
#include <string>


template<class T> struct wrapper
{
    T* m_payload = nullptr;

    template<class... Args>   // <--- шаблон функции
    T* emplace(Args&& ...args)   // <--- универсальные ссылки
    {
        // идеальная передача аргументов
        this->m_payload = new T( std::forward<Args>(args)... );
        return this->m_payload;  
    }
};

 
int main()
{
    using wrap = wrapper<std::string>;
    
    std::string old = "hello";

    std::cout << *wrap().emplace(old) << '\n';
    std::cout << *wrap().emplace("world") << '\n';
}

обрати внимание:
при грамотном использовании, std::forward параметризуется параметром шаблона.


Название: Re: rvalue и operator ->
Отправлено: ssoft от Декабрь 25, 2018, 10:59
есть такая фраза: пьяный проспиццо, дурак - никогда(ц) Петр I

Ну хоть теперь ясно, с кем дело имеем ;D.
Челом бьем).

твоя обертка со своим оператором 'стрелочка' косит под указатель.
не удивительно, что ты столкнулся с специфичным для указателей поведением.

Не обертка косит, а отсутствие оператора '.' заставляет использовать оператор '->' и мириться со специфичным для указателей поведением.

нет, не верно.
не нужно путать понятие "шаблон класса" и "шаблон функции".
...

Что ж, хотите, пишите так

Код
C++ (Qt)
struct Test
{
   void foo () && { ::std::cout << "Hello rvalue foo method." << ::std::endl; }
   void bar () && { ::std::cout << "Hello rvalue bar method." << ::std::endl; }
};
 

Код
C++ (Qt)
template<class T> struct wrapper
{
   T m_value;
 
   void exec () &&
   {
       ::std::move( m_value ).foo();
       ::std::move( m_value ).bar();
       // 100500 times using of ::std::move( m_value )
   }
};
 

вместо

Код
C++ (Qt)
template<class T> struct wrapper
{
   T m_value;
 
   void exec () &&
   {
       ::std::forward< T && >( m_value ).foo();
       ::std::forward< T && >( m_value ).bar();
   }
};
 

Даже отговаривать не буду)).


Название: Re: rvalue и operator ->
Отправлено: ViTech от Декабрь 25, 2018, 11:32
В случае с std::forward экземпляр _Bers таки истину глаголит. На сколько я понимаю, вне контекста шаблонных аргументов функции(с forwarding references (https://en.cppreference.com/w/cpp/language/reference#Forwarding_references)), std::forward особого смысла не имеет (ну разве что короче, чем static_cast). Правда, std::move тоже static_cast, так что с точки зрения реализации тут спорить особо не о чем :).


Название: Re: rvalue и operator ->
Отправлено: _Bers от Декабрь 25, 2018, 11:34
Код
C++ (Qt)
template<class T> struct wrapper
{
   T m_value;
   void exec () &&
   {
       ::std::forward< T && >( m_value ).foo();
       ::std::forward< T && >( m_value ).bar();
   }
};
 
Даже отговаривать не буду)).

Пётр I был чертовски прав.


Название: Re: rvalue и operator ->
Отправлено: _Bers от Декабрь 25, 2018, 11:39
Правда, std::move тоже static_cast, так что с точки зрения реализации тут спорить особо не о чем :).

точка зрения реализации - это вообще монопенисуальный фактор.
значение имеет дизайн использования.

std::move очень простым и недвусмысленным образом отражает намерения автора кода.





Название: Re: rvalue и operator ->
Отправлено: ssoft от Декабрь 25, 2018, 11:40
В случае с std::forward экземпляр _Bers таки истину глаголит. На сколько я понимаю, вне контекста шаблонных аргументов функции(с forwarding references (https://en.cppreference.com/w/cpp/language/reference#Forwarding_references)), std::forward особого смысла не имеет (ну разве что короче, чем static_cast). Правда, std::move тоже static_cast, так что с точки зрения реализации тут спорить особо не о чем :).

Да, но в данном случае есть еще аргумент this, тип которого и указывается явно для метода (void exec () &&).
И forward и move по сути static_cast, но бесконечно перемещать объект для доступа к rvalue функциональности, как то некрасиво.

Да и при желании можно обобщить так

Код
C++ (Qt)
template<class T> struct wrapper
{
   T m_value;
 
   void exec () &
   {
       ::std::forward< T & >( m_value ).foo();
       ::std::forward< T & >( m_value ).bar();
   }
   void exec () const &&
   {
       ::std::forward< T const && >( m_value ).foo();
       ::std::forward< T const && >( m_value ).bar();
   }
   // ...
};
 

С move такой фокус не прокатит.


Название: Re: rvalue и operator ->
Отправлено: _Bers от Декабрь 25, 2018, 11:49
С move такой фокус не прокатит.

и это совершенно правильно.
нельзя переместить неизменяемый объект.
он же неизменяемый, Карл!

сама хотелка совершить такое - это какой то бред далеко за гранью здравого смысла.




Название: Re: rvalue и operator ->
Отправлено: ssoft от Декабрь 25, 2018, 11:57
С move такой фокус не прокатит.
и это совершенно правильно.
нельзя переместить неизменяемый объект.
он же неизменяемый, Карл!

сама хотелка совершить такое - это какой то бред далеко за гранью здравого смысла.

Ну хоть в чем то мы согласны), Карл!
Используйте forward, Карл, если нужно такое специфическое поведение, Карл!


Название: Re: rvalue и operator ->
Отправлено: ViTech от Декабрь 25, 2018, 12:19
Да и при желании можно обобщить так

Код
C++ (Qt)
template<class T> struct wrapper
{
   T m_value;
 
   void exec () &
   {
       ::std::forward< T & >( m_value ).foo();
       ::std::forward< T && >( m_value ).bar();
   }
   void exec () const &&
   {
       ::std::forward< T const && >( m_value ).foo();
       ::std::forward< T const && >( m_value ).bar();
   }
};
 

Можно. А можно и так:
Код
C++ (Qt)
template<class T> struct wrapper
{
   T m_value;
 
   void exec () &
   {
       static_cast< T & >( m_value ).foo();
       static_cast< T && >( m_value ).bar();
   }
   void exec () const &&
   {
       static_cast< T const && >( m_value ).foo();
       static_cast< T const && >( m_value ).bar();
   }
};

Это если к семантике придираться :). По смыслу, std::forward у меня больше ассоциируется с передачей аргумента функции куда-то дальше. Данный же случай больше похож на доступ к объекту, так что ни move ни forward особо не подходят, а выражается именно преобразование объекта к нужному типу.


Название: Re: rvalue и operator ->
Отправлено: ssoft от Декабрь 25, 2018, 12:32
Это если к семантике придираться :). По смыслу, std::forward у меня больше ассоциируется с передачей аргумента функции куда-то дальше. Данный же случай больше похож на доступ к объекту, так что ни move ни forward особо не подходят, а выражается именно преобразование объекта к нужному типу.

Так тоже семантически понятно).
А использование move может и с толку сбить. Перемещаем - перемещаем, переместить не можем).


Название: Re: rvalue и operator ->
Отправлено: _Bers от Декабрь 25, 2018, 12:41
По смыслу, std::forward у меня больше ассоциируется с передачей аргумента функции куда-то дальше.

для этого он и был придуман.

Данный же случай больше похож на доступ к объекту, так что ни move ни forward особо не подходят, а выражается именно преобразование объекта к нужному типу.

просится что-то явно-говорящее:
Код:
access_to_temporary(m_value);






Название: Re: rvalue и operator ->
Отправлено: _Bers от Декабрь 25, 2018, 12:42
Используйте forward, Карл, если нужно такое специфическое поведение, Карл!

оно нафиг не нужно, Карл!
нет разницы временный объект или нет, если он - константный, Карл!



Название: Re: rvalue и operator ->
Отправлено: ViTech от Декабрь 25, 2018, 12:45
А использование move может и с толку сбить. Перемещаем - перемещаем, переместить не можем).

move тоже надо согласно смыслу употреблять, а не как в моём первом ответе (http://www.prog.org.ru/index.php?topic=32287.msg238249#msg238249) :). Я таким иногда грешу, когда лень static_cast писать.

А свершится перемещение или нет, также зависит и от принимающей стороны.


Название: Re: rvalue и operator ->
Отправлено: Авварон от Декабрь 25, 2018, 16:52
move и _есть_ статик каст к T&&
forward - это статик каст к & или && зависимости от того, какой тип у аргумента (void foo(Type &&t); - t это lvalue, а не rvalue)
Но у меня вопрос, внутри && ф-ии мемберы же тоже lvalue, нет? И спор о семантике (мув или форвард) бесполезен, форвард просто ничего не сделает.
И второй вопрос - а это вообще ок переиспользовать мувнутый объект? :) foo() && может спокойно разрушить внутреннее состояние и bar() && отработает на пустом объекте


Название: Re: rvalue и operator ->
Отправлено: ssoft от Декабрь 25, 2018, 18:05
Но у меня вопрос, внутри && ф-ии мемберы же тоже lvalue, нет? И спор о семантике (мув или форвард) бесполезен, форвард просто ничего не сделает.

Как ни странно, но внутри && функций мемберы lvalue, в то время как в случае доступа через оператор '.' rvalue.

Код
C++ (Qt)
wrapper< Test >().m_value; // rvalue
 

Пока не нашел объяснение, почему это так.
Стандарт регламентирует применение свойств rvalue/lvalue к членам класса, подобно классификаторам const, volatile.
Тем не менее, это не выполняется для методов &&, а для const, volatile выполняется).

И второй вопрос - а это вообще ок переиспользовать мувнутый объект? Улыбающийся foo() && может спокойно разрушить внутреннее состояние и bar() && отработает на пустом объекте

Реализация методов подобных foo() && преследуют ограничения возможности их вызова и ничего больше. Про разрушение речь не идет.

Код
C++ (Qt)
Test().foo(); // Ok
Test test;
test.foo(); // Error
static_cast< Test && >( test ).foo(); // Ok
 

move всего лишь подчеркивает намерение перемещения.

Код
C++ (Qt)
::std::move( value ).foo(); // что и куда перемещаем? что имеется в виду?
container.append( ::std::move( value ) ); // содержимое value перемешается в контейнер и, возможно, сам value некорректен.
 



Название: Re: rvalue и operator ->
Отправлено: _Bers от Декабрь 25, 2018, 18:25
Но у меня вопрос, внутри && ф-ии мемберы же тоже lvalue, нет?
да.  
поскольку у этих мемберов есть имена.

попробую проиллюстрировать на примере:

Код:
//казалось бы, должна принимать любые объекты, имеющие тип int&& 
void foo(int&&)
{}

int main()
{
    int&& v = std::move(10);

    // error: cannot bind ‘int’ lvalue to ‘int&&’
    foo(v);
}

казалось бы: типы совпадают, чего ему надо???
почему это rvalue-reference внезапно оказалась lvalue ?

потому что стандарт говорит:
временные объекты - это объекты, у которых нет имени.

Код:
// не просто выполняет преобразование к rvalue-reference
// она создает контекст, в рамках которого у xvalue нет имени
set_unigue( std::move(v) );





Название: Re: rvalue и operator ->
Отправлено: _Bers от Декабрь 25, 2018, 18:32
И второй вопрос - а это вообще ок переиспользовать мувнутый объект?

стандарт говорит, что перемещенный объект находится в состоянии unspecified

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

то бишь, ты имеешь полное право назначить перемещенному объекту новое состояние,
и продолжать с ним работать как ни в чем не бывало.

Код:
process( std::move(stdstring_value) );

// <--- unspecified

stdstring_value = "hello";

//<--- regular state



Название: Re: rvalue и operator ->
Отправлено: ssoft от Декабрь 25, 2018, 19:09
Но у меня вопрос, внутри && ф-ии мемберы же тоже lvalue, нет?
да. 
поскольку у этих мемберов есть имена.

В примере

Код
C++ (Qt)
Test().m_value;
static_cast< Test && >( test ).m_value;
::std::move( test ).m_value;
 

тоже присутствует имя m_value, но это всё rvalue значения.

И, еще отмечу, что move без потребителя не является перемещением, отсюда и появилась моя первоначальная ремарка (здесь (http://www.prog.org.ru/index.php?topic=32287.msg238483#msg238483)).


Название: Re: rvalue и operator ->
Отправлено: _Bers от Декабрь 26, 2018, 11:09
В примере
тоже присутствует имя m_value, но это всё rvalue значения.

тёмная сторона с++.
мир тонких граблей, двусмысленных трактовок,
и поведений у различных компиляторов ждут отважного путешественника
на этой стязе.

простой пример:

известный факт: константная ссылка на временный объект подливает время его жизни
до конца жизни самой ссылки.

https://ideone.com/mKHcSL

Код:
#include <iostream>

struct data
{
    ~data() { std::cout << "data: destructor\n"; }
};

int main()
{
    std::cout << "begin...\n";
    {
     const auto& ref = data{};
     std::cout << "work with temporary...\n";
    }
    std::cout << "finished!\n";
}


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

Код:
#include <iostream>

struct data
{
    ~data() { std::cout << "data: destructor\n"; }
};

struct sample
{
    data value;

    ~sample() { std::cout << "sample: destructor\n"; }
};

int main()
{
    std::cout << "begin...\n";
    {
     const auto& ref = sample().value;
     std::cout << "work with temporary...\n";
    }
    std::cout << "finished!\n";
}
вывод в консоль:
Код:
begin...
work with temporary...
sample: destructor
data: destructor
finished!

продлевается жизнь всего агрегата.
отлично! - скажешь ты, - это логично.
раз время жизни мембера продлевается,
тогда нужно продлить время жизни и всего агрегата,
ведь без хозяина мембер существовать не может.

теперь оставляем в классе хозяине только POD-типы мемберов:

Код:
#include <iostream>

struct data
{
    ~data() { std::cout << "data: destructor\n"; }
};

struct sample
{
//data value;
int value;

    ~sample() { std::cout << "sample: destructor\n"; }
};

int main()
{
    std::cout << "begin...\n";
    {
     const auto& ref = sample().value;
     std::cout << "work with temporary...\n";
    }
    std::cout << "finished!\n";
}
вывод в консоль:
Код:
begin...
sample: destructor
work with temporary...
finished!

и о боже! деструктор агрегата отработал раньше, чем закончилось время жизни константной ссылки!
в этом случае мы получаем протухшую ссылку.
привет багам!

что примечательно:
на разных компиляторах с разными настройками оптимизации можно получить разные результаты.

вывод:
ну его нафиг: связываться с "временными мемберами".

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


И, еще отмечу, что move без потребителя не является перемещением, отсюда и появилась моя первоначальная ремарка (здесь (http://www.prog.org.ru/index.php?topic=32287.msg238483#msg238483)).

строго говоря, само название "move" не вполне, казалось бы, отражает свою функцию.
факт перемещения следует из контекста применения мува,
но не содержится в самом муве.


для чего вы хотите задействовать методы временного объекта?
какие вообще плюшки вы хотите поиметь таким образом?

на самом деле единственная плюшка временного объекта - факт, что он временный.
а значит его можно переместить.

во всех остальных случаях его "временность" не имеет никакого значения.

отсюда прямиком вытекает:
вам не нужен метод временного объекта только для доступа к мемберу агрегата.
такой метод нужен либо для того, что бы можно было переместить мембер,
либо не нужен вовсе.



Название: Re: rvalue и operator ->
Отправлено: ssoft от Декабрь 26, 2018, 12:00
для чего вы хотите задействовать методы временного объекта?
какие вообще плюшки вы хотите поиметь таким образом?

Моя цель - реализовать обертку над любым пользовательским типом данных, которая бы максимально была похоже на сам пользовательский тип, но добавляла дополнительные фитчи (особенности).
К таким особенностям относятся, например,
- выбор размещения: по месту владельца/в куче.
- ленивые вычисления: copy-on-write и т.п.
- потокобезопасность
- и др.

По отдельности, вместе, в любом порядке применения.

Так как предполагается возможность использования любого пользовательского типа, то приходится учитывать все возможные гипотетические случаи, которые могут быть реализованы (возможно кем-то в бреду) с помощью конструкций языка C++.
К такому случаю относится и возможность запрета языковыми конструкциями вызова методов для lvalue/rvalue значений.
Нужен он пользовательскому типу или нет, это дело самого пользователя. В конце концов, что не запрещено, то разрешено).
Язык позволяет такой сценарий применения, в котором не подразумевается никакой move, а только запрет вызова bar() с помощью lvalue значения

Код
C++ (Qt)
Test func ();
func().bar(); // Oк
Test test;
test.bar(); // Error
 

Обертка вокруг Test должна предоставлять ровно тоже самое.


Название: Re: rvalue и operator ->
Отправлено: _Bers от Декабрь 26, 2018, 17:21
Обертка вокруг Test должна предоставлять ровно тоже самое.
пускай возвращает ресурс по ссылке, будет ровно тоже самое.