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

Является std:: move (* this) хорошим шаблоном?

Чтобы заставить этот код с ссылочными квалификаторами С++ 11 работать должным образом, я должен ввести std::move(*this), который звучит неправильно.

#include<iostream>
struct A{
    void gun() const&{std::cout << "gun const&" << std::endl;}
    void gun() &&{std::cout << "gun&&" << std::endl;}
    void fun() const&{gun();}
    void fun() &&{std::move(*this).gun();} // <-- is this correct? or is there a better option
};

int main(){
    A a; a.fun(); // prints gun const&
    A().fun(); // prints gun&&
}

Что-то не так в этом. Нужен ли std::move? Это рекомендуемое использование? На данный момент, если я не использую его, я получаю gun const& в обоих случаях, что не является ожидаемым результатом.

(Кажется, что *this является неявным и всегда имеет ссылку на lvalue, что имеет смысл, но тогда единственный способ избежать использования move)

Протестировано с clang 3.4 и gcc 4.8.3.


РЕДАКТИРОВАТЬ: Это то, что я понимаю из ответа @hvd:

1) std::move(*this) является синтаксически и концептуально правильным

2) Однако, если gun не является частью желаемого интерфейса, нет причин перегружать его версиями lv-ref и rv-ref. И две функции с разными именами могут выполнять одну и ту же работу. В конце концов, ref-определители имеют значение на уровне интерфейса, который обычно является только публичной частью.

struct A{
    private:
    void gun() const{std::cout << "gun const&" << std::endl;}
    void gun_rv(){std::cout << "gun called from fun&&" << std::endl;}
    public:
    void fun() const&{gun();}
    void fun() &&{gun_rv();} // no need for 'std::move(*this)'.
};

Но опять же, если gun является частью (универсального) интерфейса, тогда std::move(*this) необходим, но только тогда. Кроме того, даже если gun не является частью интерфейса, есть преимущества читабельности, заключающиеся в том, что функция gun не разделяется на две функции с разными именами, и стоимость этого, ну..., std::move(*this).

РЕДАКТИРОВАНИЕ 2: в ретроспективе это похоже на случай С++ 98 с перегрузкой const и no- const одной и той же функции. В некоторых случаях имеет смысл использовать const_cast (другую форму приведения), чтобы не повторять код и иметь две функции с одинаковым именем (qaru.site/info/29860/...)

4b9b3361

Ответ 1

Да, *this всегда является lvalue, независимо от того, как вызывается функция-член, поэтому, если вы хотите, чтобы компилятор рассматривал его как rvalue, вам нужно использовать std::move или эквивалент. Это должно быть, учитывая этот класс:

struct A {
  void gun() &; // leaves object usable
  void gun() &&; // makes object unusable

  void fun() && {
    gun();
    gun();
  }
};

Создание *this rvalue предполагает, что fun первый вызов gun может оставить объект непригодным. Второй звонок затем потерпит неудачу, возможно, плохо. Это не то, что должно произойти неявно.

Это та же самая причина, почему внутри void f(T&& t), t - значение l. В этом отношении, *this ничем не отличается от любого параметра опорной функции.