What are the most typical use cases of "rvalue references for *this" which the standard also calls reference qualifiers for member functions?
By the way, there is a really good explanation about this language feature here.
What are the most typical use cases of "rvalue references for *this" which the standard also calls reference qualifiers for member functions?
By the way, there is a really good explanation about this language feature here.
调用时,每个成员函数都有一个隐含的对象参数来*this
引用。
所以(a)这些正常的函数重载:
void f(const T&);
void f(T&&);
当被称为喜欢时f(x)
;(b) 这些成员函数重载:
struct C
{
void f() const &;
void f() &&;
};
当被调用时x.f()
- (a) 和 (b) 都以相似的生存能力和排名调度。
所以用例本质上是相同的。它们将支持移动语义优化。在右值成员函数中,您实际上可以掠夺对象资源,因为您知道它是一个过期对象(即将被删除):
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
}
例如:
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
}
}
某些操作在右值上调用时可能更有效,因此在值类别上重载*this
允许自动使用最有效的实现,例如
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
,但尚未正式提出 AFAIK。)
某些操作调用右值没有意义,因此重载 on*this
允许删除右值形式:
struct Foo
{
void mutate()&;
void mutate()&& = delete;
};
我实际上还不需要使用这个特性,但也许我会发现它的更多用途,因为我关心的两个编译器都支持它。
在我的编译器框架(即将发布 Sometime Soon™)中,您将诸如令牌之类的信息项传递到编译器对象中,然后调用finalize
以指示流的结束。
不调用就销毁对象会很糟糕finalize
,因为它不会清除所有输出。然而finalize
不能由析构函数完成,因为它可以抛出异常,同样finalize
如果解析器已经中止,请求更多输出也是错误的。
在所有输入已经被另一个对象封装的情况下,最好将输入传递给右值编译器对象。
pile< lexer, parser >( output_handler ).pass( open_file( "source.q" ) );
如果没有特殊支持,这一定是不正确的,因为finalize
没有被调用。界面根本不应该让用户做这样的事情。
首先要做的是排除finalize
永远不会被调用的情况。如果使用左值引用限定符调整原型,则不允许使用上面的示例,如下所示:
void pass( input_file f ) & {
process_the_file();
}
这为添加另一个正确完成对象的重载提供了空间。它是右值引用限定的,因此只有在对即将到期的对象调用时才会选择它。
void pass( input_file f ) && {
pass( std::move( f ) ); // dispatch to lvalue case
finalize();
}
现在用户几乎不需要担心记住调用finalize
,因为大多数编译器对象最终都被实例化为临时对象。
请注意,这类事情并非特定于具有 ref 资格的成员。任何函数都可以对t &
和有单独的重载t &&
。目前实际实现的方式pass
使用完美转发,然后回溯以确定正确的语义:
template< typename compiler, typename arg >
void pass( compiler && c, arg a ) {
c.take_input( a );
if ( ! std::is_reference< compiler >::value ) {
c.finalize();
}
}
有很多方法可以处理重载。实际上,不合格的成员函数在不关心调用它们的对象的类别(左值或右值)以及不将该信息传递给函数方面是不寻常的。除了隐式之外的任何函数参数都this
必须说明其参数的类别。