![](/img/trans.png)
[英]What does the single ampersand after the parameter list of a member function declaration mean?
[英]What does the & (ampersand) at the end of member function signature mean?
我不记得是哪个演讲了,但最近我看了一些来自 CppCon 2017 的演讲,有人提到作为某种旁注,重载operator=
的唯一真正方法是以下方式:
class test {
public:
test& operator=(const test&) &;
};
他明确强调了尾随&
但没有说明它的作用。
那么它有什么作用呢?
Ref-qualifiers 不是 C++17 特性(查看问题的标签),而是 C++11 中引入的特性。
struct Foo
{
void bar() const & { std::cout << "const lvalue Foo\n"; }
void bar() & { std::cout << "lvalue Foo\n"; }
void bar() const && { std::cout << "const rvalue Foo\n"; }
void bar() && { std::cout << "rvalue Foo\n"; }
};
const Foo&& getFoo() { return std::move(Foo()); }
int main()
{
const Foo c_foo;
Foo foo;
c_foo.bar(); // const lvalue Foo
foo.bar(); // lvalue Foo
getFoo().bar(); // [prvalue] const rvalue Foo
Foo().bar(); // [prvalue] rvalue Foo
// xvalues bind to rvalue references, and overload resolution
// favours selecting the rvalue ref-qualifier overloads.
std::move(c_foo).bar(); // [xvalue] const rvalue Foo
std::move(foo).bar(); // [xvalue] rvalue Foo
}
请注意,右值可用于初始化 const 左值引用(并因此扩展由右值标识的对象的生命周期),这意味着如果我们从上面的示例中删除右值引用限定符重载,则右值值类别在示例中都将支持剩余的const &
重载:
struct Foo
{
void bar() const & { std::cout << "const lvalue Foo\n"; }
void bar() & { std::cout << "lvalue Foo\n"; }
};
const Foo&& getFoo() { return std::move(Foo()); }
int main()
{
const Foo c_foo;
Foo foo;
// For all rvalue value categories overload resolution
// now selects the 'const &' overload, as an rvalue may
// be used to initialize a const lvalue reference.
c_foo.bar(); // const lvalue Foo
foo.bar(); // lvalue Foo
getFoo().bar(); // const lvalue Foo
Foo().bar(); // const lvalue Foo
std::move(c_foo).bar(); // const lvalue Foo
std::move(foo).bar(); // const lvalue Foo
}
有关简要介绍,请参见例如以下博客文章:
const
&
重载为了可能解释你从 CppCon 谈话中回忆的引述的意图,
“……这是重载
operator=
的唯一真正方法……”
我们访问[over.match.funcs]/1, /4 & /5 [强调我的]:
/1 [over.match.funcs] 的子条款描述了在使用重载决议的每个上下文中提交给重载决议的候选函数集和参数列表。 ...
/4对于非静态成员函数,隐式对象参数的类型为
(4.1) — “对 cv
X
左值引用”,用于未使用 ref 限定符或使用&
ref 限定符声明的函数(4.2) — 使用
&&
ref 限定符声明的函数的“对 cvX
右值引用”其中
X
是函数所属的类,cv 是成员函数声明中的 cv 限定。 .../5 ... 对于没有引用限定符声明的非静态成员函数,附加规则适用:
- (5.1) — 即使隐式对象参数不是 const 限定的,只要参数在所有其他方面都可以转换为隐式对象参数的类型,就可以将右值绑定到参数。 [ 注意:这样的参数是右值这一事实不会影响隐式转换序列的排名。 — 尾注 ]
从上面的 /5,以下重载(其中显式&
ref 限定符已被省略)
struct test
{
test& operator=(const test&) { return *this }
}
允许为 r 值赋值,例如
int main()
{
test t1;
t1 = test(); // assign to l-value
test() = t1; // assign to r-value
}
但是,如果我们使用&
ref 限定符显式声明重载,则[over.match.funcs]/5.1不适用,并且只要我们不提供使用&&
ref 限定符声明的重载,r 值赋值将不被允许。
struct test
{
test& operator=(const test&) & { return *this; }
};
int main()
{
test t1;
t1 = test(); // assign to l-value
test() = t1; // error [clang]: error: no viable overloaded '='
}
我不会对在声明自定义赋值运算符重载时明确包含&
ref 限定符是否是“重载operator=
的唯一真正方法”发表任何意见,但我敢推测,然后我猜测背后的意图这样的声明是对-r-value 赋值的排除。
作为一个正确设计的赋值运算符应该永远不会是const
( const T& operator=(const T&) const &
没有多大意义),并且由于右值不能用于初始化非常量左值引用,一组重载对于仅包含T& operator=(const T&) &
的给定类型T
,for operator=
永远不会提供可从标识为右值值类别的T
对象调用的可行重载。
根据http://en.cppreference.com/w/cpp/language/member_functions成员函数声明之后的&
是lvalue ref-qualifier
。
换句话说,它要求this
是一个左值(隐式对象参数具有对 cv 限定的 X 的左值引用类型)。 还有&&
,它要求this
是一个 r 值。
从文档中复制( const-、volatile- 和 ref-qualified 成员函数):
#include <iostream>
struct S {
void f() & { std::cout << "lvalue\n"; }
void f() &&{ std::cout << "rvalue\n"; }
};
int main(){
S s;
s.f(); // prints "lvalue"
std::move(s).f(); // prints "rvalue"
S().f(); // prints "rvalue"
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.