Разве это не приведёт к dangling reference? Параметр функции всё равно что локальная переменная. Вы предлагаете вернуть ссылку на локальный объект.
Мне кажется тут надо по значению возвращать, оставив однако мув в return
Здравствуйте, Sm0ke, Вы писали:
S>Разве это не приведёт к dangling reference? Параметр функции всё равно что локальная переменная.
Не думаю. "Все равно что локальная переменная" -- это когда у нас параметр передается по значению, т.е.:
int & f(int a) {
return a;
}
В случае же когда параметр передается по ссылке, мы просто возвращаем эту же самую ссылку и на нее действуют обычные правила жизни.
Когда мы имеем дело с rvalue reference, то rvalue reference должно оставаться валидным в течении жизни выражения, в котором эта ссылка появилась. Т.е.:
T && operator<<(T && d, int v) { ...; return std::move(d); }
(T{} << 0) << 1;
сперва у нас создается временный объект T и rvalue reference на него передается в первый вызов operator<<. Он эту же самую ссылку возвращает. И эта ссылка остается валидной, т.к. выражение у нас не завершилось. Когда вызывается второй operator<<, то эта ссылка отдается туда же. И там она опять же валидна, т.к. выражение все еще не завершилось.
Re[2]: MSVC: A non-const reference may only be bound to an lvalue
prog.cc: In function 'UnsignedSet&& operator<<(UnsignedSet&&, unsigned int)':
prog.cc:16:12: error: cannot bind rvalue reference of type 'UnsignedSet&&' {aka 'std::set<unsigned int>&&'} to lvalue of type 'UnsignedSet' {aka 'std::set<unsigned int>'}
16 | return us; //std::move(us);
| ^~
Re[3]: MSVC: A non-const reference may only be bound to an l
Здравствуйте, so5team, Вы писали:
S>В случае же когда параметр передается по ссылке, мы просто возвращаем эту же самую ссылку и на нее действуют обычные правила жизни.
S>Когда мы имеем дело с rvalue reference, то rvalue reference должно оставаться валидным в течении жизни выражения, в котором эта ссылка появилась. Т.е.: S>
S>сперва у нас создается временный объект T и rvalue reference на него передается в первый вызов operator<<. Он эту же самую ссылку возвращает. И эта ссылка остается валидной, т.к. выражение у нас не завершилось. Когда вызывается второй operator<<, то эта ссылка отдается туда же. И там она опять же валидна, т.к. выражение все еще не завершилось.
Время жизни временного объекта продлевается при привязке его на rvalue reference локальной переменной (или параметра) и откладывается до момента, когда переменная выйдет из scope. В данном случае время жизни параметра o заканчивается после выхода из функции some_fn() .
Иначе пример выше выдал бы сперва "going to return" из main(), а потом "~obj_t".
Тобишь когда выражение { obj_t && tmp = some_fn( obj_t{} ); } отработает, то tmp будет dangling ref.
При бинде одной rvalue ref от другой rvalue ref время жизни временного объекта дальше не продлевается. Только при бинде от самого временного объекта.
Здравствуйте, Sm0ke, Вы писали:
S>>Когда мы имеем дело с rvalue reference, то rvalue reference должно оставаться валидным в течении жизни выражения, в котором эта ссылка появилась. Т.е.: S>>
S>>сперва у нас создается временный объект T и rvalue reference на него передается в первый вызов operator<<. Он эту же самую ссылку возвращает. И эта ссылка остается валидной, т.к. выражение у нас не завершилось. Когда вызывается второй operator<<, то эта ссылка отдается туда же. И там она опять же валидна, т.к. выражение все еще не завершилось.
S>Вы ошибаетесь. Рассмотрим пример:
S>
Здравствуйте, Videoman, Вы писали:
V>Нет. Вот что думает на это счёт стандартная библиотека:
V>template<class T>
V>const T& max(const T& a, const T& b)
V>{
V> return (a < b) ? b : a;
V>}
V>
Чтож, читаем notes по вашей ссылке:
Notes
Capturing the result of std::max by reference produces a dangling reference if one of the parameters is a temporary and that parameter is returned:
int n = 1;
const int& r = std::max(n — 1, n + 1); // r is dangling
При передаче в эту функцию временного объекта и при привязке по const ref результата будет провисшая ссылка на уже разрушенный объект.
Так что будьте осторожней со стандартным максом
Re[4]: MSVC: A non-const reference may only be bound to an lvalue
Здравствуйте, so5team, Вы писали:
S>Здравствуйте, _NN_, Вы писали:
_NN>>А зачем здесь нужен std::move ? _NN>>std::move всего лишь приводит тип к UnsignedSet&&, который и так у нас есть.
S>https://wandbox.org/permlink/opM2owLr51OIDWaM
S>
S>prog.cc: In function 'UnsignedSet&& operator<<(UnsignedSet&&, unsigned int)':
S>prog.cc:16:12: error: cannot bind rvalue reference of type 'UnsignedSet&&' {aka 'std::set<unsigned int>&&'} to lvalue of type 'UnsignedSet' {aka 'std::set<unsigned int>'}
S> 16 | return us; //std::move(us);
S> | ^~
S>
Действительно.
Не задумывался об этом совсем.
Получается в С++ как только есть именнованный объект он сразу lvalue и нужно явно его возвращать в rvalue.
struct A {};
void f(A&& a)
{
A&& b = a;
}
<source>(4): error C2440: 'initializing': cannot convert from 'A' to 'A &&'
<source>(4): note: You cannot bind an lvalue to an rvalue reference
Здравствуйте, Sm0ke, Вы писали:
S>Чтож, читаем notes по вашей ссылке:
S>
S>Notes
S>Capturing the result of std::max by reference produces a dangling reference if one of the parameters is a temporary and that parameter is returned:
S>int n = 1;
S>const int& r = std::max(n — 1, n + 1); // r is dangling
S>При передаче в эту функцию временного объекта и при привязке по const ref результата будет провисшая ссылка на уже разрушенный объект. S>Так что будьте осторожней со стандартным максом
Это да. Но поинт в том, что пока std::max работает, у вас валидная ссылка. Иначе бы вот такой вот пример не работал бы:
#include <string>
#include <iostream>
using namespace std::string_literals;
int main()
{
auto v = std::max(std::max(std::max("One"s, "Two"s), "Three"s), "Zero"s);
std::cout << v << std::endl;
}
Но он работает.
Потому что в выражении, где std::max вызываются, ссылки валидные. Но когда выражение завершается, уничтожаются все временные объекты, созданные в процессе его выполнения, поэтому ссылка и протухает.
Именно из-за этого ссылку нельзя сохранять. Но вот передать ее в конструктор объекта (как в примере) можно, т.к. она все еще валидная.
Re[6]: MSVC: A non-const reference may only be bound to an l
Здравствуйте, so5team, Вы писали:
S>Это да. Но поинт в том, что пока std::max работает, у вас валидная ссылка. Иначе бы вот такой вот пример не работал бы: S>
S>#include <string>
S>#include <iostream>
S>using namespace std::string_literals;
S>int main()
S>{
S> auto v = std::max(std::max(std::max("One"s, "Two"s), "Three"s), "Zero"s);
S> std::cout << v << std::endl;
S>}
S>
S>Но он работает.
S>Потому что в выражении, где std::max вызываются, ссылки валидные. Но когда выражение завершается, уничтожаются все временные объекты, созданные в процессе его выполнения, поэтому ссылка и протухает.
S>Именно из-за этого ссылку нельзя сохранять. Но вот передать ее в конструктор объекта (как в примере) можно, т.к. она все еще валидная.
Понял, спасибо.
Но вот в чём могут быть подводные камни с предложением сделать так из исходной задачи
Здравствуйте, пффф, Вы писали:
П>Как это всё правильно переписать?
Я конечно дико извиняюсь, но, может, лучше написать функцию, которая конструирует множество по значению из списка аргументов (россыпью или initializer_list)?
И дать ей имя какое-нибудь другое, вместо неконстантного "оператора вдвига".
Тем более, что std::set умеет принимать список
serializeUnsignedSet(UnsignedSet{1, 2, 3, 4, 5});
Перекуём баги на фичи!
Re[2]: MSVC: A non-const reference may only be bound to an lvalue
Здравствуйте, Кодт, Вы писали:
К>Я конечно дико извиняюсь, но, может, лучше написать функцию, которая конструирует множество по значению из списка аргументов (россыпью или initializer_list)? К>И дать ей имя какое-нибудь другое, вместо неконстантного "оператора вдвига".
К>Тем более, что std::set умеет принимать список К>
S>Время жизни временного объекта продлевается при привязке его на rvalue reference локальной переменной (или параметра) и откладывается до момента, когда переменная выйдет из scope. В данном случае время жизни параметра o заканчивается после выхода из функции some_fn() .
Нет, время жизни заканчивается после полного выполнения выражения obj_t && tmp = some_fn( obj_t{} );, т.е. после присвоения ссылки. Но вы и сами это понимаете: S>Иначе пример выше выдал бы сперва "going to return" из main(), а потом "~obj_t". S>Тобишь когда выражение { obj_t && tmp = some_fn( obj_t{} ); } отработает, то tmp будет dangling ref. S>При бинде одной rvalue ref от другой rvalue ref время жизни временного объекта дальше не продлевается. Только при бинде от самого временного объекта. S>Корректный пример:
Во это тоже корректный пример:
obj_t tmp = some_fn( obj_t{} ); // tmp не бита, не ссылка
S>obj_t default
S>obj_t move
S>~obj_t
S>going to return
S>~obj_t
S>
Причём с тем же результатом: S>К сожалению объект класса obj_t тут будет создан дважды. S>Сперва default constructor, потом move constructor, потом дважды destructor. S>В случае вектора — мув конструктор считается относительно дешёвым.
Но, если посмотреть дальше, то можно заметить пессимизацию при возвращении объекта.
Рассмотри выражение:
some_fn(some_fn(some_fn( obj_t{} )));
Если some_fn возвращает объект, то при каждом выходе из some_fn будет создаваться новый объект, если же some_fn возвращает ссылку, то пересоздания объекта не будет вовсе. Это может быть важно при использовании operator<<
Так что это проблема не в функции, а в её использовании.