为什么不允许仅在一个 ref-qualifier 上重载?
Why is overloading on just one ref-qualifier not allowed?
显然,不允许重载 ref-qualifiers – 如果删除 &
或 &&
(只是令牌,而不是它们的功能):
#include <iostream>
struct S {
void f() & { std::cout << "Lvalue" << std::endl; }
void f() && { std::cout << "Rvalue" << std::endl; }
};
int main()
{
S s;
s.f(); // prints "Lvalue"
S().f(); // prints "Rvalue"
}
换句话说,如果您有两个同名同类型的函数,如果您定义其中一个,则必须同时定义这两个函数。我认为这是故意的,但是原因是什么?为什么不允许调用 &&
版本的右值(如果它已定义),以及 "primary" f()
在以下变体中的所有其他内容(反之亦然 - 尽管这会令人困惑):
struct S {
void f() { std::cout << "Lvalue" << std::endl; }
void f() && { std::cout << "Rvalue" << std::endl; }
};
换句话说,让它们的行为类似于关于主模板的模板特化。
跟下面的情况没什么区别:
struct S {};
void g(S s);
void g(S& s);
int main()
{
S s;
g(s); // ambiguous
}
过载解决方案一直都是这样工作的;按引用传递不如按值传递(反之亦然)。
(引用限定函数的重载解析就像一个普通函数一样,带有一个隐式第一个参数,其参数是 *this
;左值引用限定就像第一个参数 S &
, const &
就像 S const &
等)
我猜你是说g(s)
应该调用g(S&)
而不是模棱两可。
我不知道确切的原理,但重载决议已经足够复杂了,因为它没有添加更多的特殊情况(尤其是那些可能会静默编译为不是编码器预期的情况)。
正如您在问题中所指出的,使用 S &
和 S &&
这两个版本可以轻松避免该问题。
您可以同时拥有两者,也可以两者兼有。 There is no specific requirement to include both if you implement the one.
要注意的是,未标记限定符的成员方法(非静态成员)适用于左值和右值。 一旦你用引用限定符重载一个方法,除非你也标记其他方法,否则你 运行 会陷入歧义问题。
During overload resolution, non-static cv-qualified member function of class X
is treated as a function that takes an implicit parameter of type lvalue reference to cv-qualified X
if it has no ref-qualifiers or if it has the lvalue ref-qualifier. Otherwise (if it has rvalue ref-qualifier), it is treated as a function taking an implicit parameter of type rvalue reference to cv-qualified X
.
所以基本上,如果您有一种合格的方法(例如,对于左值 &
)和一种不合格的方法,则规则是这样的,它们实际上都是合格的,因此是模棱两可的。
类似的原理适用于 const
限定符。您可以实现一个方法,一个 "version" 用于 const
对象,一个用于非 const
对象。标准库容器就是很好的例子,特别是 begin()
、end()
和其他与迭代器相关的方法。
一个特定的用例是当对象是临时(或过期)对象和不是临时(或过期)对象时应用于方法的逻辑不同。如果您知道生命周期即将结束,您可能希望在内部优化掉某些调用和数据处理。
另一个是将方法的使用限制为左值。如果实体即将过期或者是临时的,则某个应用程序或对象逻辑可能没有意义或没有用。
来自 §13.4.1/4 的标准(取自 N4567 草案)中的措辞是:
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
让我们从定义不带任何 ref 限定符的基本非静态成员函数意味着什么开始。
§13.3.1 [4]
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
等等,还有更多。
[5] 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.
因此
显然,不允许重载 ref-qualifiers – 如果删除 &
或 &&
(只是令牌,而不是它们的功能):
#include <iostream>
struct S {
void f() & { std::cout << "Lvalue" << std::endl; }
void f() && { std::cout << "Rvalue" << std::endl; }
};
int main()
{
S s;
s.f(); // prints "Lvalue"
S().f(); // prints "Rvalue"
}
换句话说,如果您有两个同名同类型的函数,如果您定义其中一个,则必须同时定义这两个函数。我认为这是故意的,但是原因是什么?为什么不允许调用 &&
版本的右值(如果它已定义),以及 "primary" f()
在以下变体中的所有其他内容(反之亦然 - 尽管这会令人困惑):
struct S {
void f() { std::cout << "Lvalue" << std::endl; }
void f() && { std::cout << "Rvalue" << std::endl; }
};
换句话说,让它们的行为类似于关于主模板的模板特化。
跟下面的情况没什么区别:
struct S {};
void g(S s);
void g(S& s);
int main()
{
S s;
g(s); // ambiguous
}
过载解决方案一直都是这样工作的;按引用传递不如按值传递(反之亦然)。
(引用限定函数的重载解析就像一个普通函数一样,带有一个隐式第一个参数,其参数是 *this
;左值引用限定就像第一个参数 S &
, const &
就像 S const &
等)
我猜你是说g(s)
应该调用g(S&)
而不是模棱两可。
我不知道确切的原理,但重载决议已经足够复杂了,因为它没有添加更多的特殊情况(尤其是那些可能会静默编译为不是编码器预期的情况)。
正如您在问题中所指出的,使用 S &
和 S &&
这两个版本可以轻松避免该问题。
您可以同时拥有两者,也可以两者兼有。 There is no specific requirement to include both if you implement the one.
要注意的是,未标记限定符的成员方法(非静态成员)适用于左值和右值。 一旦你用引用限定符重载一个方法,除非你也标记其他方法,否则你 运行 会陷入歧义问题。
During overload resolution, non-static cv-qualified member function of class
X
is treated as a function that takes an implicit parameter of type lvalue reference to cv-qualifiedX
if it has no ref-qualifiers or if it has the lvalue ref-qualifier. Otherwise (if it has rvalue ref-qualifier), it is treated as a function taking an implicit parameter of type rvalue reference to cv-qualifiedX
.
所以基本上,如果您有一种合格的方法(例如,对于左值 &
)和一种不合格的方法,则规则是这样的,它们实际上都是合格的,因此是模棱两可的。
类似的原理适用于 const
限定符。您可以实现一个方法,一个 "version" 用于 const
对象,一个用于非 const
对象。标准库容器就是很好的例子,特别是 begin()
、end()
和其他与迭代器相关的方法。
一个特定的用例是当对象是临时(或过期)对象和不是临时(或过期)对象时应用于方法的逻辑不同。如果您知道生命周期即将结束,您可能希望在内部优化掉某些调用和数据处理。
另一个是将方法的使用限制为左值。如果实体即将过期或者是临时的,则某个应用程序或对象逻辑可能没有意义或没有用。
来自 §13.4.1/4 的标准(取自 N4567 草案)中的措辞是:
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
让我们从定义不带任何 ref 限定符的基本非静态成员函数意味着什么开始。
§13.3.1 [4]
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
等等,还有更多。
[5] 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.
因此