Вызов std :: move для возвращаемого значения - какой должна быть подпись - PullRequest
0 голосов
/ 29 ноября 2018

Рассмотрим

class X
{
public:
    std::unique_ptr<int> m_sp;
    A m_a;

    A test1()
    {
        return std::move(m_a);
    }

    A&& test2()
    {
        return std::move(m_a);
    } 

    std::unique_ptr<int> test3()
    {
        return std::move(m_sp);
    }

    std::unique_ptr<int>&& test4()
    {
        return std::move(m_sp);
    }

    std::unique_ptr<int> test5()
    {
        return std::make_unique<int>(50);
    }
};

class A
{
public:
    A()
    {
        m_i = 1;
    }

    A(A&& other)
    {
        this->m_i = other.m_i;
        other.m_i = -1;
    }

    A& operator=(A&& other)
    {
        this->m_i = other.m_i;
        other.m_i = -1;
        return *this;
    }

    int m_i;
};

Чтобы выполнить эти классы

X x;
A y;
y.m_i = 10;
y = x.test1();

X x2;
A y2;
y2.m_i = 10;
y2 = x2.test2();

, оба вызывают назначение перемещения А, но только в случае test1 мы вызываем конструктор перемещения А.Это почему?Это потому, что мы не можем вернуть A && (std :: move приведет A к A &&, но test1 говорит, что должно вернуть A).

В общем, когда кто-то хочет переместить / передать право собственности на дорогие переменные-члены, хотите ли вы, чтобы возвращаемое значение было типом rvalue-reference (A &&) или lvalue (A)?

Это выглядит немного неестественно, как будто вы не используете переменные-члены, вы позволяете RVO / NRVO делать свое дело и просто возвращаете lvalue.Например, в случае с unique_ptr, когда у вас есть автоматическая переменная, у вас есть сигнатура, подобная test5 (), но если у вас есть переменная, не подходящая для RVO / NRVO, например, член varaible, если подпись test3 или test4 предпочтительнее.

Интересно знать.

Спасибо

1 Ответ

0 голосов
/ 29 ноября 2018

Там есть семантическая разница.Когда вы возвращаете объект, как в

A test1()
{
    return std::move(m_a);
}

std::unique_ptr<int> test3()
{
    return std::move(m_sp);
}

, вы всегда выходите из своего члена.Независимо от того, делает ли вызывающий объект что-то с возвращаемым значением или нет, вы переместитесь из вашего X во временное.Владение больше не принадлежит вам.Вызывающий может принять возвращаемое значение.Если вызывающая сторона игнорирует возвращаемое значение, временное будет все равно уничтожено.Если вы возвращаете rvalue-ссылку, с другой стороны, как в

A&& test2()
{
    return std::move(m_a);
}

std::unique_ptr<int>&& test4()
{
    return std::move(m_sp);
}

, вы просто предлагаете вызывающей стороне возможность перейти от / взять на себя владение объектом.Если вызывающая сторона не выполняет перемещение, ваш X сохранит право собственности, объект не будет перемещен.

Важно понять, что вопреки тому, что следует из названия, std::move() делаетна самом деле не выполнять ход.Это просто позволяет перемещать данный объект.Фактическое перемещение выполняется конструктором перемещения или оператором присваивания перемещения соответствующего типа.

Итак, ваш ответ: это зависит от того, что вы хотите выразить.Если вы возвращаете объект, вы говорите: «Я выбрасываю это, если вы хотите: оно там».Если вы возвращаете ссылку на rvalue, вы говорите: «Это то, что есть, теперь у вас есть шанс воспользоваться этим, иначе я оставлю это»…

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...