Имеет ли смысл функция возвращать ссылку на rvalue?

Какой будет действительный вариант использования такой подписи?:

T&& foo();

Или rvalue ref предназначен только для использования в качестве аргумента?

Как можно использовать такую функцию?

T&& t = foo(); // is this a thing? And when would t get destructed?

Ответы

Ответ 1

Для бесплатной функции не имеет смысла возвращать ссылку на rvalue. Если это нестатический локальный объект, то вы никогда не захотите возвращать ссылку или указатель на него, потому что он будет уничтожен после возврата из функции. Возможно, имеет смысл возвращать rvalue-ссылку на объект, который вы передали функции. Это действительно зависит от варианта использования, если имеет смысл или нет.

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

class foo
{
    std::vector<int> bar;
public:
    foo(int n) : bar(n) {}
    std::vector<int>& get_vec() { return bar; }
};

Если вы делаете

auto vec = foo(10).get_vec();

Вы должны скопировать, потому что get_vec возвращает lvalue. Если вы вместо этого используете

class foo
{
    std::vector<int> bar;
public:
    foo(int n) : bar(n) {}
    std::vector<int>& get_vec() & { return bar; }
    std::vector<int>&& get_vec() && { return std::move(bar); }
};

Тогда vec сможет переместить вектор, возвращаемый get_vec и вы сэкономите дорогостоящую операцию копирования.

Ответ 2

T&& t = foo(); // is this a thing? And when would t get destructed?

Ссылка на rvalue действительно похожа на ссылку на lvalue. Думайте о своем примере, как будто это были нормальные ссылки:

T& foo();

T& t = foo(); // when is t destroyed?

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

Тот же ответ по-прежнему относится к вашему справочному примеру.


Но... имеет ли смысл возвращать ссылку на значение?

Иногда да. Но очень редко.

учти это:

std::vector<int> v = ...;

// type is std::tuple<std::vector<int>&&>
auto parameters = std::forward_as_tuple(std::move(v));

// fwd is a rvalue reference since std::get returns one.
// fwd is valid as long as v is.
decltype(auto) fwd = std::get<0>(std::move(parameters));

// useful for calling function in generic context without copying
consume(std::get<0>(std::move(parameters)));

Так что да, есть пример. Вот еще один интересный:

struct wrapper {

    auto operator*() & -> Heavy& {
        return heavy;
    }

    auto operator*() && -> Heavy&& {
        return std::move(heavy);
    }

private:
    Heavy instance;
};

// by value
void use_heavy(Heavy);

// since the wrapper is a temporary, the
// Heavy contained will be a temporary too. 
use_heavy(*make_wrapper());

Ответ 3

Я думаю, что в случае использования было бы явно дать разрешение "очистить" некоторые нелокальные переменные. Возможно, что-то вроде этого:

class Logger
{
public:
    void log(const char* msg){
        logs.append(msg);
    }
    std::vector<std::string>&& dumpLogs(){
        return std::move(logs);
    }
private:
    std::vector<std::string> logs;
};

Но я признаю, что сделал это сейчас, я никогда не использовал его, и это также можно сделать так:

std::vector<std::string> dumpLogs(){
    auto dumped_logs = logs;
    return dumped_logs;
}