operator=
的方法应该是这样的:class test {
public:
test& operator=(const test&) &;
};
他明确强调了尾随的
&
,但没有说明它的作用。那么它到底是做什么的呢?
operator=
的方法应该是这样的:class test {
public:
test& operator=(const test&) &;
};
&
,但没有说明它的作用。引用限定符不是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 &
重载。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演讲中提到的话语意图,
"... the only true way of overloading
operator=
..."
我们查看 [over.match.funcs]/1, /4 & /5 [强调是我的]:
/1[over.match.funcs]的子条款描述了在每个上下文中提交给重载分辨率的候选函数集和参数列表。...
/4对于非静态成员函数,隐式对象参数的类型为
(4.1) — 函数声明没有引用限定符或者有
&
引用限定符时,“cv&”。(4.2) — 函数声明带有
&&
引用限定符时,“cv&&”。其中T是函数成员所属的类,cv是成员函数声明上的限定符。
/5 ...对于没有引用限定符的非静态成员函数,还有一条额外规则适用:
- (5.1) — 即使隐式对象参数没有被const限定,只要在所有其他方面上能将参数转换为隐式对象参数的类型,就可以将rvalue绑定到该参数上。[注释:这样的参数是rvalue并不影响隐式转换序列的排名。-结束备注]
根据上述/5,以下重载函数是可行的(省略了显式的 &
引用限定符):
struct test
{
test& operator=(const test&) { return *this }
}
允许将值赋给r-value,例如:
int main()
{
test t1;
t1 = test(); // assign to l-value
test() = t1; // assign to r-value
}
然而,如果我们明确声明带有 &
引用限定符的重载函数,[over.match.funcs]/5.1 不适用,并且只要我们不提供使用 &&
引用限定符声明的重载函数,就不允许进行右值赋值操作。
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-qualifier发表任何意见,但是如果我敢猜测,那么我会猜测这样一个声明背后的意图是排除对to-r-value赋值的影响。
由于一个正确设计的赋值运算符应该永远不会是const
(const T& operator=(const T&) const &
没有多少意义),并且rvalue不能用于初始化非const lvalue引用,因此对于给定类型T
的operator=
一组仅包含T& operator=(const T&) &
的重载将永远无法提供可行的重载,可以从被识别为rvalue值类别的T
对象调用。
&
是lvalue ref-qualifier
。this
是一个l-value(隐式对象参数具有cv限定符X的lvalue引用类型)。还有&&
,它要求this
是一个r-value。#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"
}
const &
在func参数后面)、常规lvalue(&
)、const rvalue(const &&
)或常规rvalue(&&
)进行重载。来源:这是我对此答案的总结:https://dev59.com/0FYN5IYBdhLWcg3wuaJx#47003980。 - Gabriel Staplesconst &
重载函数,因为rvalue可以用于初始化const lvalue引用。 - dfrib