c++ - 什么是 "rvalue reference for *this"?

标签 c++ c++11 move-semantics c++-faq qualifiers

在 clang 的 C++11 status page 中遇到了一个名为“*this 的右值引用”的提议。 .

我已经阅读了很多关于右值引用并理解它们的内容,但我认为我不知道这一点。我也无法使用这些术语在网络上找到很多资源。

页面上有提案文件的链接:N2439 (将 move 语义扩展到 *this),但我也没有从那里得到太多示例。

这个功能是关于什么的?

最佳答案

首先,“*this 的引用限定符”只是一个“营销声明”。 *this 的类型永远不会改变,请参阅这篇文章的底部。不过用这种措辞更容易理解。

接下来,下面的代码根据函数的“隐式对象参数”的ref-qualifier选择要调用的函数:

// t.cpp
#include <iostream>

struct test{
  void f() &{ std::cout << "lvalue object\n"; }
  void f() &&{ std::cout << "rvalue object\n"; }
};

int main(){
  test t;
  t.f(); // lvalue
  test().f(); // rvalue
}

输出:

$ clang++ -std=c++0x -stdlib=libc++ -Wall -pedantic t.cpp
$ ./a.out
lvalue object
rvalue object

当调用函数的对象是一个右值(例如,未命名的临时对象)时,整个过程允许您利用这一事实。再以如下代码为例:

struct test2{
  std::unique_ptr<int[]> heavy_resource;

  test2()
    : heavy_resource(new int[500]) {}

  operator std::unique_ptr<int[]>() const&{
    // lvalue object, deep copy
    std::unique_ptr<int[]> p(new int[500]);
    for(int i=0; i < 500; ++i)
      p[i] = heavy_resource[i];

    return p;
  }

  operator std::unique_ptr<int[]>() &&{
    // rvalue object
    // we are garbage anyways, just move resource
    return std::move(heavy_resource);
  }
};

这可能有点做作,但你应该明白了。

请注意,您可以将 cv-qualifiers(constvolatile)和 ref-qualifiers( &&&)。


注意:后面有很多标准引号和重载解析说明!

† 要了解这是如何工作的,以及为什么@Nicol Bolas 的回答至少部分错误,我们必须深入研究 C++ 标准(解释为什么@Nicol 的回答错误的部分在底部,如果你只对那个感兴趣)。

将调用哪个函数由称为重载解析的过程确定。这个过程相当复杂,所以我们只会触及对我们重要的部分。

首先,重要的是要了解成员函数的重载解析是如何工作的:

§13.3.1 [over.match.funcs]

p2 The set of candidate functions can contain both member and non-member functions to be resolved against the same argument list. So that argument and parameter lists are comparable within this heterogeneous set, a member function is considered to have an extra parameter, called the implicit object parameter, which represents the object for which the member function has been called. [...]

p3 Similarly, when appropriate, the context can construct an argument list that contains an implied object argument to denote the object to be operated on.

为什么我们甚至需要比较成员函数和非成员函数?运算符重载,这就是原因。考虑一下:

struct foo{
  foo& operator<<(void*); // implementation unimportant
};

foo& operator<<(foo&, char const*); // implementation unimportant

您肯定希望以下调用免费函数,不是吗?

char const* s = "free foo!\n";
foo f;
f << s;

这就是为什么成员函数和非成员函数都包含在所谓的重载集中的原因。为了使解决方案不那么复杂,标准引用的粗体部分存在。此外,这对我们来说很重要(同一个子句):

p4 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. [...]

p5 During overload resolution [...] [t]he implicit object parameter [...] retains its identity since conversions on the corresponding argument shall obey these additional rules:

  • no temporary object can be introduced to hold the argument for the implicit object parameter; and

  • no user-defined conversions can be applied to achieve a type match with it

[...]

(最后一点只是意味着您不能根据调用成员函数(或运算符)的对象的隐式转换来欺骗重载决议。)

让我们以本文开头的第一个示例为例。在上述转换之后,重载集看起来像这样:

void f1(test&); // will only match lvalues, linked to 'void test::f() &'
void f2(test&&); // will only match rvalues, linked to 'void test::f() &&'

然后,包含隐含对象参数的参数列表与重载集中包含的每个函数的参数列表进行匹配。在我们的例子中,参数列表将只包含该对象参数。让我们看看它是什么样子的:

// first call to 'f' in 'main'
test t;
f1(t); // 't' (lvalue) can match 'test&' (lvalue reference)
       // kept in overload-set
f2(t); // 't' not an rvalue, can't match 'test&&' (rvalue reference)
       // taken out of overload-set

如果在测试集合中的所有重载之后,只剩下一个,则重载解析成功,并调用链接到转换后的重载的函数。第二次调用 'f' 也是如此:

// second call to 'f' in 'main'
f1(test()); // 'test()' not an lvalue, can't match 'test&' (lvalue reference)
            // taken out of overload-set
f2(test()); // 'test()' (rvalue) can match 'test&&' (rvalue reference)
            // kept in overload-set

但是请注意,如果我们没有提供任何 ref-qualifier(因此没有重载函数),那么 f1 匹配一个右值(仍然是 §13.3.1):

p5 [...] For non-static member functions declared without a ref-qualifier, an additional rule applies:

  • even if the implicit object parameter is not const-qualified, an rvalue can be bound to the parameter as long as in all other respects the argument can be converted to the type of the implicit object parameter.
struct test{
  void f() { std::cout << "lvalue or rvalue object\n"; }
};

int main(){
  test t;
  t.f(); // OK
  test().f(); // OK too
}

现在,为什么@Nicol 的回答至少部分错误。他说:

Note that this declaration changes the type of *this.

错了,*this 总是是一个左值:

§5.3.1 [expr.unary.op] p1

The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.

§9.3.2 [class.this] p1

In the body of a non-static (9.3) member function, the keyword this is a prvalue expression whose value is the address of the object for which the function is called. The type of this in a member function of a class X is X*. [...]

关于c++ - 什么是 "rvalue reference for *this"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17599858/

相关文章:

c++ - 链接器找不到虚拟类 C++ 中访问的静态字段的符号

c++ - linux下keycode是否一致

c++ - 重复 move 已被 move 捕获的 lambda 中的变量

c++ - 来自可变模板的模棱两可的成员请求

c++ - 从第 3 方库导出 cpp 损坏的函数

c++ - 确定 DLL 的符号名称

c++ - 从标准库类型特征继承

c++ - 如何使用 move 语义迭代器和模板

rust - 如何从函数返回具有 String 类型字段的结构 Vec?

struct - 复制结构以传递给函数 - 此字段未实现 `Copy`