引用限定符"const &&"有什么用?

What is use of the ref-qualifier `const &&`?

本文关键字:什么 const 引用      更新时间:2023-10-16

根据上一个问题,我一直在挖掘有关修饰符的一些内容。

给定下面的代码示例;

#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 &&引用限定符在方法上的目的是什么?

该方法无法修改对象上的内容(它是const),从const &&方法尝试return std::move(abc);实际上根本不会移动std::string。假设您希望能够修改对象,因为它是一个r值并且不会存在很长时间。如果要删除const &&限定的方法,代码std::move(a3).method()将绑定到const &限定的方法,这是有意义的。

  • 如果有的话,限定为const &的方法和限定为const &&的方法之间隐含的语义差异是什么?例如,实现将如何变化,或者为什么两者都需要?
  • std::string真的能够从临时对象中"移动"出来吗?
  • 在这种情况下,std::string get() const &&的"规范"签名是什么样子的?

关于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&&方法限定符的有用性是什么?

值得注意的是,在c++库扩展提案中,optional§5.3包含了重载,例如
constexpr T value() const &&;

被限定为const&&,并被指定执行与&&替代方案相同的操作。

我可以推断出这种情况的原因;这是为了完整性和正确性。如果在右值上调用value()方法,那么无论它是否为const,它都会执行相同的操作。const需要由被移动的包含对象或使用它的客户端代码来处理。如果对象被包含在某个mutable状态中,那么该状态可以合法地更改。

这可能仍然有一些优点;

  • 声明 = delete 以禁止该方法在右值和x值上使用。
  • 如果类型具有可变状态,并且限定符在目标环境中是有意义的(可能除了其他限定符之外),请考虑使用它。
  • 如果您正在实现泛型容器类型,那么对于的完整性和正确性,考虑添加它并执行与&&方法相同的操作。这里的建议来自标准库(及其扩展)。

一个符合const&&条件的方法的"规范"签名是什么样子的?

由于该方法将执行与&&方法相同的操作,因此我建议签名与&&签名匹配。

我们可以在"const右值引用的好处是什么"一文中找到对这个问题的类似探讨。其中一个特别的用法是标准库中的这个例子:

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

同时禁用右值的refcref。我们可以在c++ 11标准草案20.8 函数对象段落2中找到这些声明。

Scott Meyers在c++ 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这样的符号,尽管实际上我会将这种方法保存为修改方法,特别是那些可能代价高昂的方法。在不检索任何东西的情况下改变然后丢弃一个对象是没有多大意义的,如果执行这个改变的代价很高,那就更没有意义了。该构造为编译器提供了一种标记和防止此类使用的方法。