Подтвердить что ты не робот

Что такое "ссылки rvalue для этого"?

Каковы наиболее типичные варианты использования "ссылок на rvalue для * this", которые стандарт также вызывает ссылочные квалификаторы для функций-членов?

Кстати, есть действительно хорошее объяснение этой языковой функции здесь.

4b9b3361

Ответ 1

При вызове каждая функция-член имеет неявный параметр объекта, который *this ссылается.

Итак, (а) эти нормальные перегрузки:

void f(const T&);
void f(T&&);

при вызове как f(x); и (b) эти перегрузки функции-члена:

struct C
{
    void f() const &;
    void f() &&;
};

когда вызывается как x.f() - оба (a) и (b) отправляются с аналогичной жизнеспособностью и ранжированием.

Таким образом, варианты использования практически одинаковы. Они должны поддерживать перемещение семантической оптимизации. В функции члена rvalue вы можете по существу разграбить ресурсы объектов, потому что знаете, что это истекающий объект (его нужно удалить):

int main()
{
    C c;
    c.f(); // lvalue, so calls lvalue-reference member f
    C().f(); // temporary is prvalue, so called rvalue-reference member f
    move(c).f(); // move changes c to xvalue, so again calls rvalue-reference member f
}

Итак, например:

struct C
{
    C operator+(const C& that) const &
    {
        C c(*this); // take a copy of this
        c += that;
        return c;
    }

    C operator+(const C& that) &&
    {
        (*this) += that;
        return move(*this); // moving this is ok here
    }
}

Ответ 2

Некоторые операции могут быть более эффективными при вызове rvalues, поэтому перегрузка в категории значений *this позволяет автоматически использовать наиболее эффективную реализацию, например.

struct Buffer
{
  std::string m_data;
public:
  std::string str() const& { return m_data; }        // copies data
  std::string str()&& { return std::move(m_data); }  // moves data
};

(Эта оптимизация может быть выполнена для std::ostringstream, но формально не была предложена AFAIK.)

Некоторые операции не имеют смысла вызывать значения r, поэтому перегрузка на *this позволяет удалить форму rvalue:

struct Foo
{
  void mutate()&;
  void mutate()&& = delete;
};

Мне еще не нужно было использовать эту функцию, но, возможно, теперь я найду для нее больше возможностей, чем два компилятора, о которых я забочусь о поддержке.

Ответ 3

В моей компоновке компилятора (который будет выпущен Sometime Soon ™) вы передаете элементы информации, такие как токены, в объект компилятора, затем вызовите finalize, чтобы указать конец потока.

Было бы плохо уничтожить объект, не вызывая finalize, потому что он не выведет весь его вывод. Тем не менее finalize не может быть выполнено деструктором, потому что он может вызывать исключение, а также неправильно спрашивать finalize для большего вывода, если парсер уже прерывается.

В случае, когда весь вход уже инкапсулирован другим объектом, приятно передать вход в объект компилятора rvalue.

pile< lexer, parser >( output_handler ).pass( open_file( "source.q" ) );

Без специальной поддержки это должно быть неверно, потому что finalize не вызывается. Интерфейс не должен позволять пользователю делать что-то вообще.

Первое, что нужно сделать, это исключить случай, когда finalize никогда не вызывается. Вышеприведенный пример запрещен, если прототип настроен с помощью l-ref ref-qualifier следующим образом:

void pass( input_file f ) & {
    process_the_file();
}

Это дает возможность добавить еще одну перегрузку, которая правильно завершает объект. Это rvalue ref-qual, поэтому он выбирается только в том случае, если вызывается на истечении объекта.

void pass( input_file f ) && {
    pass( std::move( f ) ); // dispatch to lvalue case
    finalize();
}

Теперь пользователю почти не нужно беспокоиться о том, чтобы запомнить вызов finalize, поскольку большинство объектов компилятора в конечном итоге создаются как временные.


Обратите внимание, что такого рода вещи не являются специфическими для квалифицированных членов. Любая функция может иметь отдельные перегрузки для t & и t &&. Путь pass фактически реализуется в настоящее время, использует совершенную переадресацию, а затем обратную трассировку для определения правильной семантики:

template< typename compiler, typename arg >
void pass( compiler && c, arg a ) {
    c.take_input( a );

    if ( ! std::is_reference< compiler >::value ) {
        c.finalize();
    }
}

Существует множество способов подхода к перегрузке. Фактически, неквалифицированные функции-члены необычны в том, что они не заботятся о категории (lvalue или rvalue) объекта, на который они вызваны, и не передают эту информацию в функцию. Любой параметр функции, кроме неявного this, должен сказать что-то о категории его аргумента.