什么是ref-qualifier`const&&`的使用?

时间:2014-07-18 11:55:47

标签: c++ c++11 language-lawyer move-semantics rvalue-reference

在上一个问题之后,我一直在研究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()&amp;
  get()const&amp;
  get()&amp;&amp;
  get()const&amp;&amp;

这用clang和gcc 4.9.1编译并运行(尽管不是4.9.0)。直播sample here

在一般代码中(样本用于查看代码如何编译和运行)。

  • 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 &&的“规范”签名会是什么样的?

4 个答案:

答案 0 :(得分:11)

关于const&&的用处......(一般而言)

const&&限定符对成员方法的有用性至多是最小的。不能以与&&方法允许修改对象相同的方式修改对象;毕竟它是const(如上所述,mutable确实改变了这一点)。因此,我们无法消除它的内心,因为临时任务正在到期,就像我们在类似于正常move的情况下那样。

在许多方面,const&&的有用性可以在类型const T&&的对象开始时的有用性的上下文中得到最好的评估。 const T&&函数参数有用吗?正如另一个答案(对于这个问题)here所指出的,它们在声明删除的功能方面非常有用,例如在这种情况下

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

明确禁止将prvalue和xvalue值类别类型的对象与函数一起使用,const T&&执行bind to all prvalue and xvalue objects

  

const&&方法限定符的用处是什么?

值得注意的是,在提案C++ library extensions中,optional,§5.3包含重载,例如

constexpr T value() const &&;

,其资格为const&&,并被指定执行与&&替代相同的操作。

我能推断出这个案子的原因;这是为了完整性和正确性。如果在右值上调用value()方法,则它执行与const无关的相同操作。 const将需要由正在移动的包含对象或使用它的客户端代码处理。如果存在包含对象的某个mutable状态,则可以合法地更改该状态。

这可能还有一些优点;没有特别的顺序...

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

const&&合格方法的“规范”签名会是什么样的?

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

答案 1 :(得分:9)

我们可以在文章What are const rvalue references good for?中找到对此问题的类似探索,并且突出显示的一个用途就是标准库中的这个示例:

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

完全禁用refcref rvalues。我们可以在draft C++11 standard部分20.8 功能对象 2 中找到这些声明。

Scott Meyers在Universal References in C++11中暗示了这种用法:

  

即使简单添加const限定符也足以禁用   将“&amp;&amp;”解释为普遍参考:

答案 2 :(得分:3)

假设我们有一个mutable状态的类型。然后const&&将允许我们改变该状态,并指出这种突变是安全的。

struct bar;

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

const不是绝对的。

除了可变状态之外,为了提取状态,在const方法中抛弃const&&是不安全的,因为从实际的const对象中以这种方式提取状态是不确定的行为。

答案 3 :(得分:2)

我看到了对方法进行ref-qualifying的两个主要用途。一个就像你在get() &&方法中展示的那样,你用它来选择一个可能更有效的实现,只有当你知道不再使用该对象时才能使用它。但另一个是安全提示,以防止在临时对象上调用某些方法。

在这种情况下,您可以使用get() const && = delete之类的符号,但实际上我会保存这种方法来修改方法,尤其是那些可能代价高昂的方法。在不检索某些东西的情况下变异然后丢弃一个对象没有多大意义,如果执行变异是昂贵的话,那就更加有意义了。此构造为编译器提供了标记和阻止此类使用的方法。