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

Что такое ref-qualifier `const &&`?

Я немного искал ref-qualifiers, следуя предыдущему вопросу.

Учитывая пример кода ниже;

#include <iostream>
#include <string>
#include <utility>

struct A {
  std::string abc = "abc";
  std::string& get() & {
    std::cout << "get() &" << std::endl;
    return abc;
  }
  std::string get() && {
    std::cout << "get() &&" << std::endl;
    return std::move(abc);
  }
  std::string const& get() const & {
    std::cout << "get() const &" << std::endl;
    return abc;
  }
  std::string get() const && {
    std::cout << "get() const &&" << std::endl;
    return abc;
  }
};

int main()
{
  A a1;
  a1.get();
  const A a2{};
  a2.get();
  A().get();
  const A a3{};
  std::move(a3).get();
}

И результат будет таким, как вы ожидали:

get() и
get() const и
get() &&
get() const && &

Это компилируется и работает с clang и gcc 4.9.1 (но не 4.9.0). Live пример здесь.

В общем коде (пример показывает, как код компилируется и запускается).

  • Какова была бы цель const && ref-qualifier для метода?

Метод не может изменить содержимое объекта (это const), попытка return std::move(abc); из метода const && вообще не перемещает std::string. Предположительно, вы захотите изменить объект, так как это значение r и не будет долгое время. Если метод const && должен быть удален, код std::move(a3).method() будет привязан к const & квалифицированному методу, что имеет смысл.

  • Что, если таковые имеются, подразумевала бы семантическую разницу между методом, указанным как const &, а один - как const &&? То есть как реализация будет отличаться или почему вы хотите оба?
  • Может ли std::string быть "перемещенным" из временного объекта?
  • Что будет выглядеть "каноническая" подпись для std::string get() const && в этом случае?
4b9b3361

Ответ 1

О полезности const&&... (в общем)

Полезность квалификатора const&& в методе члена в лучшем случае минимальна. Объект нельзя изменить так же, как метод && позволил бы его модифицировать; в конце концов, это const (как отмечено, mutable меняет это значение). Таким образом, мы не сможем вырвать его кишки, поскольку временное время истекает, так как мы будем что-то вроде нормального move.

Во многих отношениях полезность const&& может быть лучше всего оценена в контексте того, насколько полезен объект типа const T&&. Насколько полезен аргумент функции const T&&? Как указано в другом ответе (на этот вопрос) здесь, они очень полезны при объявлении функций, удаленных, например. в этом случае

template <class T> void ref (const T&&) = delete;

для явного запрета использования объектов класса значений значения prvalue и xvalue, а const T&& выполняет привязать ко всем объектам prvalue и xvalue.

Какова полезность квалификатора метода const&&?

Интересно отметить, что в предложении расширения библиотеки С++, optional, § 5.3, включает в себя перегрузки, например

constexpr T value() const &&;

которые квалифицируются как const&& и указаны для выполнения того же действия, что и альтернатива &&.

Причина, по которой я могу заключить этот случай; что это для полноты и правильности. Если метод value() вызывается для r-значения, то он выполняет то же действие независимо от него, что const или нет. С помощью const должен быть обработан перемещаемый объект или код клиента, используя его. Если есть некоторое состояние mutable с содержащимся объектом, то это состояние может быть законно изменено.

В этом, возможно, все-таки есть какая-то заслуга; в определенном порядке...

  • Чтобы объявить его = delete, чтобы запретить использование метода для prvalues ​​и xvalues.
  • Если тип имеет изменчивое состояние, и определитель имеет смысл (возможно, в дополнение к другим квалификаторам) в целевой среде, рассмотрите его.
  • Если вы используете общий тип контейнера, то для полноты и правильности рассмотрите возможность добавления его и выполнения того же действия, что и метод &&. Совет здесь сортируется из стандартной библиотеки (и ее расширений).

Какова была бы "каноническая" подпись для const&& квалифицированного метода?

Так как метод будет выполнять те же действия, что и метод &&, я бы сказал, что подпись соответствует подписи &&.

Ответ 2

Мы можем найти аналогичное исследование этой проблемы в статье Каковы ссылки на константу rvalue для?, и одно из них, которое выделялось, - это пример формы стандарта библиотека:

template <class T> void ref (const T&&) = delete;
template <class T> void cref (const T&&) = delete;

который отключает ref и cref для значений r вообще. Эти объявления можно найти в проекте С++ 11 в разделе 20.8 Объекты функций, абзац 2.

Скотт Мейерс ссылается на это использование в Universal References в С++ 11:

Даже простое дополнение спецификатора const достаточно, чтобы отключить интерпретация "& &" как универсальная ссылка:

Ответ 3

Предположим, что у нас есть тип с состоянием mutable. Тогда const&& будет позволять нам мутировать это состояние и указывать, что такая мутация безопасна.

struct bar;

struct foo {
  mutable std::vector<char> state;
  operator bar() const&;
  operator bar() const&&;
};

const не является абсолютным.

Запрет изменчивого состояния нецелесообразно отбрасывать const в методе const&& для извлечения состояния, потому что извлечение состояния таким образом из фактического объекта const имеет поведение undefined.

Ответ 4

Я вижу два основных применения для ref-qualifying метода. Один из них, как показано в методе get() &&, где вы используете его для выбора потенциально более эффективной реализации, доступной только тогда, когда вы знаете, что объект больше не будет использоваться. Но другой является советом безопасности, чтобы предотвратить вызов определенных методов для временных объектов.

В таких случаях вы можете использовать обозначение типа get() const && = delete, хотя реально я бы сохранил этот подход для модификации методов, особенно тех, которые потенциально дорогостоящие. Не имеет смысла мутировать, а затем отбрасывать объект, не извлекая что-то, и вдвойне, если это дорого для выполнения мутации. Эта конструкция дает компилятору способ отмечать и предотвращать такое использование.