2013-07-08 2 views
22

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

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

+0

С компиляторами, поддерживающими его сейчас, я ожидаю, что вскоре появится статья или две темы. – chris

+0

gcc только что реализовал его в 4.8.1, поэтому не многие люди стали использовать эту функцию еще. –

+0

В принципе, если вы хотите, чтобы пользователи не создавали постоянные объекты класса, а использовали только временные, вы можете добиться этого, выполнив нужные функции-члены '&&'. –

ответ

19

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

So (а) эти нормальные функции Перегрузки:

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

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

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

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

Таким образом, варианты использования практически одинаковы. Они должны поддерживать перемещение семантической оптимизации. В функции 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 
    } 
} 
+0

Также рассмотрите членов, которые не имеют смысла вызывать rvalues, как оператор присваивания. – Puppy

+0

@DeadMG: Да , значение rvalue (prvalue или xvalue) не будет связываться с void f (T &) ', поэтому варианты использования такие же, как' T :: f() & 'wrt неявный параметр объекта. обратите внимание на ортогональную связь между нормальным функциональным параметром и неявным параметром объекта - поэтому общее понимание категорий значений и выбора перегрузки по мере их применения к нормальному функциональному параметру может быть перепрофилировано как-есть для параметра неявного объекта-функции-члена (только с незначительными различия не стоит упоминать). –

+0

FWIW, я бы даже не потрудился с движением. Просто добавьте к себе и двигайтесь потом. –

5

Некоторые операции могут быть более эффективным при вызове 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.)

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

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

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

+0

«Для нестатических функций-членов, объявленных без реф-квалификатора, применяется дополнительное правило: даже если параметр неявного объекта не является константным, значение rvalue может быть привязано к параметру как , так как во всех остальных отношениях аргумент может быть преобразован в тип неявного параметра объекта. " Таким образом, 'Foo :: mutate()' без квалификатора ref жизнеспособно против rvalue этого .... –

+0

Кроме того, что касается ранжирования: «S1 и S2 являются привязками привязки (8.5.3), и ни один из них не ссылается на неявный параметр объекта a нестатическая функция-член __declared без ref-qualifier__, а S1 связывает ссылку rvalue с rvalue и S2 связывает ссылку lvalue. ". Поэтому использование rvalue будет неоднозначным между 'mutate() no-ref-qualifier' и' mutate() && '. Это было ваше намерение? –

+0

т. Е. Я думаю, что в конечном итоге вы получите нужный эффект, но диагностика может быть неидеальной (ошибка неоднозначной перегрузки). –

1

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

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

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

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

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

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

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, должен сказать что-то о категории его аргумента.

Смежные вопросы