Что такое использование ref-квалификатора ' const &&'?



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



Приведен пример кода ниже;



#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). Живая выборка здесь .



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




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


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




  • какова, если таковая имеется, подразумеваемая семантическая разница между методом, квалифицируемым как const &, и методом, квалифицируемым как const &&? То есть, как будет меняться реализация или почему вы хотите и то, и другое?

  • действительно ли std::string можно "переместить" из временного объекта?

  • как будет выглядеть "каноническая" подпись для std::string get() const && в этом случае?

669   4  

4 ответов:

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

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

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

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

Явно запретить использование объектов типов prvalue и xvalue value category с функциями, а const T&& делаетпривязку ко всем объектам prvalue и xvalue .

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

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

constexpr T value() const &&;

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

Причина, по которой я могу сделать вывод для этого случая, заключается в том, что это для полноты и правильности. Если метод value() вызывается для rvalue, то он выполняет то же самое действие независимо от того, является она const или нет. С const придется иметь дело с перемещаемым содержимым объекта или использующим его клиентским кодом. Если существует некоторое состояние mutable, в котором содержится объект, то это состояние может быть законно изменено. Вполне возможно, что в этом есть еще какая-то заслуга, но не в особом порядке...
  • объявить его = delete запретить использование метода на prvalues и xvalues.
  • Если тип имеет изменяемое состояние и квалификатор имеет смысл (возможно, в дополнение к другим квалификаторам) в целевой среде, рассмотрите его.
  • Если вы реализуете универсальный контейнерный тип, то для полноты и корректности рассмотрите возможность его добавления и выполнения того же действия, что и метод&&. Совет здесь-сортировка из стандартной библиотеки (и ее расширений).

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

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

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

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

, который отключает ref и cref для rvalues в целом. Мы можем найти эти объявления в разделе проект стандарта C++11 20.8 функциональные объекты абзац 2.

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

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

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

struct bar;

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

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

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

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

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

Comments

    Ничего не найдено.