Russian Qt Forum
Ноябрь 25, 2024, 14:12 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 [2] 3 4   Вниз
  Печать  
Автор Тема: rvalue и operator ->  (Прочитано 24519 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #15 : Ноябрь 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 && выйдет из области видимости?

Ну ладно, а (оптимальные) методы здесь каким боком?
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #16 : Ноябрь 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).
Временному объекту "продляют жизнь" если на него имеется ссылка. Областью видимости такого временного объекта является область видимости вызова метода.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #17 : Ноябрь 26, 2018, 12:18 »


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

Вообще-то 11й стандарт говорит о том, когда применяется RVO и copy-elision.
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #18 : Ноябрь 26, 2018, 12:34 »

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

К стандарту С++17 относится: Guaranteed Copy Elision. Осталось только, чтобы компиляторы полно и точно соответствовали стандартам Улыбающийся.
Записан

Пока сам не сделаешь...
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #19 : Декабрь 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';
}




Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #20 : Декабрь 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->?
Записан

Пока сам не сделаешь...
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #21 : Декабрь 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();
}
 
« Последнее редактирование: Декабрь 14, 2018, 08:25 от ssoft » Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #22 : Декабрь 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;
}
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #23 : Декабрь 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;
}
 
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #24 : Декабрь 14, 2018, 16:53 »

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

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

Сообщений: 486


Просмотр профиля
« Ответ #25 : Декабрь 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,
вот только зачем зазря усложнять синтаксис,
используя вещь не по назначению?

Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #26 : Декабрь 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 для последующей работы с с этим значением далее.
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #27 : Декабрь 21, 2018, 22:37 »

применение конструкции вне шаблонов не имеет особого практического смысла.

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

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

Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #28 : Декабрь 24, 2018, 14:01 »

Я, нет).

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

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

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

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

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


« Последнее редактирование: Декабрь 24, 2018, 14:56 от _Bers » Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #29 : Декабрь 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 параметризуется параметром шаблона.
« Последнее редактирование: Декабрь 24, 2018, 15:10 от _Bers » Записан
Страниц: 1 [2] 3 4   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.603 секунд. Запросов: 23.