首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >什么是“*this的右值引用”?

什么是“*this的右值引用”?
EN

Stack Overflow用户
提问于 2013-07-08 15:35:09
回答 3查看 2K关注 0票数 24

标准也称为成员函数的引用限定符的“*this的右值引用”最典型的用例是什么?

顺便说一句,关于这个语言特性here有一个很好的解释。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-07-08 18:21:01

调用时,每个成员函数都有一个*this引用的隐式对象参数。

所以(a)这些正常的函数重载:

代码语言:javascript
复制
void f(const T&);
void f(T&&);

当像f(x)一样调用时;以及(b)这些成员函数重载:

代码语言:javascript
复制
struct C
{
    void f() const &;
    void f() &&;
};

当像x.f()一样调用时- (a)和(b)调度都具有类似的可行性和排名。

因此,用例基本上是相同的。它们是为了支持移动语义优化。在rvalue成员函数中,您实际上可以掠夺对象资源,因为您知道它是一个即将过期的对象(即将被删除):

代码语言:javascript
复制
int main()
{
    C c;
    c.f(); // lvalue, so calls lvalue-reference member f
    C().f(); // temporary is prvalue, so called rvalue-reference member f
    move(c).f(); // move changes c to xvalue, so again calls rvalue-reference member f
}

举个例子:

代码语言:javascript
复制
struct C
{
    C operator+(const C& that) const &
    {
        C c(*this); // take a copy of this
        c += that;
        return c;
    }

    C operator+(const C& that) &&
    {
        (*this) += that;
        return move(*this); // moving this is ok here
    }
}
票数 21
EN

Stack Overflow用户

发布于 2013-07-08 19:39:35

某些操作在调用rvalue时效率更高,因此*this的value类别上的重载允许自动使用最有效的实现,例如

代码语言:javascript
复制
struct Buffer
{
  std::string m_data;
public:
  std::string str() const& { return m_data; }        // copies data
  std::string str()&& { return std::move(m_data); }  // moves data
};

(这种优化可以针对std::ostringstream完成,但还没有正式提出。)

有些操作调用rvalue没有意义,因此在*this上重载允许删除rvalue表单:

代码语言:javascript
复制
struct Foo
{
  void mutate()&;
  void mutate()&& = delete;
};

我实际上还不需要使用这个特性,但是现在我关心的两个编译器支持它,也许我会发现它有更多的用途。

票数 5
EN

Stack Overflow用户

发布于 2013-07-08 21:28:01

在我的编译器框架(不久将发布™)中,将令牌等信息项传递到编译器对象中,然后调用finalize来指示流的结束。

在不调用finalize的情况下销毁一个对象是不好的,因为它不会刷新所有的输出。然而,析构函数不能完成finalize,因为它可能抛出异常,同样,如果解析器已经中止,那么要求finalize提供更多输出也是错误的。

在所有输入都已经被另一个对象封装的情况下,将输入传递给rvalue编译器对象是很好的。

代码语言:javascript
复制
pile< lexer, parser >( output_handler ).pass( open_file( "source.q" ) );

如果没有特殊的支持,这一定是不正确的,因为finalize不会被调用。界面根本不应该让用户做这样的事情。

首先要做的是排除finalize永远不会被调用的情况。如果使用左值引用限定符调整原型,则上面的示例是不允许的:

代码语言:javascript
复制
void pass( input_file f ) & {
    process_the_file();
}

这为添加另一个重载提供了空间,该重载可以正确地完成对象。它是右值ref限定的,因此只有在即将到期的对象上调用时才会选择它。

代码语言:javascript
复制
void pass( input_file f ) && {
    pass( std::move( f ) ); // dispatch to lvalue case
    finalize();
}

现在,用户几乎不需要担心要记住调用finalize,因为大多数编译器对象最终都被实例化为临时对象。

注意,这类事情并不是ref限定的成员所特有的。对于t &t &&,任何函数都可以有单独的重载。pass目前的实际实现方式是使用完美的转发,然后回溯来确定正确的语义:

代码语言:javascript
复制
template< typename compiler, typename arg >
void pass( compiler && c, arg a ) {
    c.take_input( a );

    if ( ! std::is_reference< compiler >::value ) {
        c.finalize();
    }
}

有许多方法可以处理重载。实际上,非限定成员函数通常不关心被调用的对象的类别(左值或右值),也不会将该信息传递给函数。除了隐式this之外,任何函数参数都必须说明其参数的类别。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17521238

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档