Оптимизация перемещения или именованного возвращаемого значения (NRVO)? - PullRequest
28 голосов
/ 04 июня 2011

Допустим, у нас есть следующий код:

std::vector<int> f()
{
  std::vector<int> y;
  ...
  return y;
} 

std::vector<int> x = ...
x = f();

Кажется, у компилятора есть два подхода:

(a) NRVO: уничтожить x, затем построить x () вместо x.
(b) Перемещение: создайте f () во временном пространстве, переместите f () в x, уничтожьте f ().

Может ли компилятор использовать любой из подходов в соответствии со стандартом?

1 Ответ

39 голосов
/ 04 июня 2011

Компилятор может NRVO во временное пространство или переместить конструкцию во временное пространство.Оттуда он будет перемещаться, назначая x.

Обновление:

Каждый раз, когда вы испытываете желание оптимизировать с использованием ссылок rvalue, и вы не уверены вВ результате создайте себе пример класса, который отслеживает его состояние:

  • построено
  • построено по умолчанию
  • перемещено из
  • уничтожено

И проведите этот класс через ваш тест.Например:

#include <iostream>
#include <cassert>

class A
{
    int state_;
public:
    enum {destructed = -2, moved_from, default_constructed};

    A() : state_(default_constructed) {}
    A(const A& a) : state_(a.state_) {}
    A& operator=(const A& a) {state_ = a.state_; return *this;}
    A(A&& a) : state_(a.state_) {a.state_ = moved_from;}
    A& operator=(A&& a)
        {state_ = a.state_; a.state_ = moved_from; return *this;}
    ~A() {state_ = destructed;}

    explicit A(int s) : state_(s) {assert(state_ > default_constructed);}

    friend
    std::ostream&
    operator<<(std::ostream& os, const A& a)
    {
        switch (a.state_)
        {
        case A::destructed:
            os << "A is destructed\n";
            break;
        case A::moved_from:
            os << "A is moved from\n";
            break;
        case A::default_constructed:
            os << "A is default constructed\n";
            break;
        default:
            os << "A = " << a.state_ << '\n';
            break;
        }
        return os;
    }

    friend bool operator==(const A& x, const A& y)
        {return x.state_ == y.state_;}
    friend bool operator<(const A& x, const A& y)
        {return x.state_ < y.state_;}
};

A&& f()
{
    A y;
    return std::move(y);
}

int main()
{
    A a = f();
    std::cout << a;
}

Если это поможет, поместите операторы печати в специальные элементы, которые вас интересуют (например, конструктор копирования, конструктор перемещения и т. Д.).

Кстати, еслиэто порождает тебя, не волнуйся.Это тоже для меня.Таким образом, этот конкретный дизайн (возвращающий rvalue ссылку на локальную переменную) не является хорошим дизайном.В вашей системе вместо segfaulting может выводиться «A уничтожено».Это было бы еще одним признаком того, что вы не хотите этого делать.

...