函数 && 限定符行为

function && qualifier behaviour

我被下面的代码搞糊涂了:

struct test {
  void f() & {
    std::cout << "&" << std::endl;
  }
  void f() const& {
    std::cout << "const&" << std::endl;
  }
  void f() && {
    std::cout << "&&" << std::endl;
  }
  void f() const&& {
    std::cout << "const&&" << std::endl;
  }

  void g() & {
    std::cout << "& -> ";
    f();
  }
  void g() const& {
    std::cout << "const& -> " ;
    f();
  }
  void g() && {
    std::cout << "&& -> ";
    f();
  }
  void g() const&& {
    std::cout << "const&& -> ";
    f();
  }

  test() {} //allow default const construction
};

int main(int, char**) {
    test value;
    const test constant;

    value.g();
    constant.g();

    std::move(value).g();
    std::move(constant).g();
}

当我用 clang 3.5 编译时,我得到这个输出:

& -> &
const& -> const&
&& -> &
const&& -> const&

为什么此处删除了 r 值限定符?有什么方法可以使用正确的限定符从 g 调用 f 吗?

嗯,对于 f(),正确的限定符总是 lvalue reference,因为您在 this 上使用了它。你所有的 f 调用都是 this->f()this 总是 lvalue。对于外部世界,对象是 rvalue this 是对象内部的左值并不重要。

这是因为解引用1this总是产生一个左值,不管它是否指向临时对象。

所以当你这样写的时候:

f();

其实是这个意思:

this->f(); //or (*this).f()
           //either way 'this' is getting dereferenced here

因此从 g() 调用为 lvalue 编写的 f() 的重载 — 并相应地应用常量正确性。

希望对您有所帮助。


1.请注意 this 是纯右值;只有在取消引用后它才会产生左值。

ref-qualifiers 影响隐式对象参数,§13.3.1/4:

For non-static member functions, the type of the implicit object parameter is

  • “lvalue reference to cv X” for functions declared without a ref-qualifier or with the & ref-qualifier
  • “rvalue reference to cv X” for functions declared with the && ref-qualifier

where X is the class of which the function is a member and cv is the cv-qualification on the member function declaration.

粗略地说,重载解析是在对象参数到对象参数转换上执行的,就像任何其他参数->参数转换一样。
但是,f() 被转换为 (*this).f() (§9.3.1/3),而 *this 是一个左值。 §5.3.1/1:

The unary * operator performs indirection: […] and the result is an lvalue referring to the object or function to which the expression points.

因此 f& 限定符的重载是首选 - 事实上,右值重载完全被忽略,因为右值对象引用的初始化器必须是右值(§8.5 中的最后一个要点.3/5).此外,在重载决策中,非 const 引用优于 const 引用(§13.3.3.2/3,关于标准转换的最后一个要点)。

调用 f() 被解释为 (*this).f()。取消引用指针的结果始终是一个左值,因此 *this 是一个左值,并且调用了左值限定函数。

这种行为甚至是有道理的,至少对我而言。大多数情况下,右值表达式引用的对象将在完整表达式(临时)或当前范围(我们选择 std::move 的自动局部变量)的末尾销毁).但是当你 inside 一个成员函数时,这些都不是真的,所以对象不应该把自己当作右值,可以这么说。这也是为什么将右值引用函数参数的名称作为函数内的左值是有意义的。

如果你想调用右值限定的f,你可以这样做:

std::move(*this).f();