为什么只允许在一个 ref 限定符上重载
Why is overloading on just one ref-qualifier not allowed?
显然,不允许在 ref 限定符上重载——如果您删除 &
或 &&
(只是标记,而不是它们的函数(,则此代码将无法编译:
#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"
}
换句话说,如果您有两个相同名称和类型的函数,则在定义任何一个函数时都必须定义这两个函数。我认为这是故意的,但原因是什么?例如,为什么不允许为右值调用&&
版本(如果已定义(,并且"主要"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
}
重载解析一直以这种方式工作;按引用传递不优于按值传递(反之亦然(。
(ref 限定函数的重载解析就像它是一个普通函数一样,其参数是 *this
的隐式第一个参数;限定 lvalue-ref 就像第一个参数S &
,const &
就像S const &
等(
我猜你是说g(s)
应该打电话给g(S&)
而不是模棱两可。
我不知道确切的基本原理,但是重载解决方案已经足够复杂了,因为它没有添加更多特殊情况(尤其是那些可能默默编译为不是编码人员意图的情况(。
正如您在问题中指出的那样,通过使用两个版本可以轻松避免该问题 S &
和 S &&
.
您可以同时拥有两者,也可以其中之一。如果实现一个,则没有包含两者的特定要求。
问题是未标有限定符的成员方法(非静态成员(适合与左值和右值一起使用。一旦使用引用限定符重载方法,除非也标记其他方法,否则会遇到歧义问题。
在重载解析期间,类
X
的非静态 cv 限定成员函数被视为一个函数,该函数采用 lvalue 类型的隐式参数引用到 cv 限定X
(如果它没有 ref 限定符或具有 lvalue ref 限定符(。否则(如果它具有右值引用限定符(,则将其视为采用右值类型的隐式参数引用的函数,以引用 cv 限定的X
。
所以基本上,如果你有一个合格的方法(例如,对于左值&
(和一个不合格的方法,规则是这样的,它们实际上是合格的,因此是模棱两可的。
类似的原理也适用于const
限定符。您可以实现一个方法,并为const
对象提供一个"版本",为非const
对象提供一个"版本"。标准库容器就是很好的例子,特别是begin()
、end()
和其他迭代器相关的方法。
一个特定的用例是,当应用于方法的逻辑在对象是临时(或即将过期(对象时与不是对象时不同时。如果您知道生命周期即将结束,您可能希望在内部优化某些调用和数据处理。
另一种方法是将方法的使用限制为左值。如果实体即将过期或临时,则某个应用程序或对象逻辑可能没有意义或有用。
§13.4.1/4 中的标准(取自 N4567 草案(中的措辞是:
对于非静态成员函数,隐式对象参数的类型为
"对 cv X 的左值引用",用于声明没有 ref-qualifier 或 & ref-qualifier 的函数
"对 cv X 的右值引用",用于使用 &&ref-限定符声明的函数
让我们从定义一个没有任何 ref 限定符的基本非静态成员函数开始。
§13.3.1 [4]
对于非静态成员函数,隐式对象参数的类型为
— ">对 cv X 的左值引用",用于声明没有 ref-qualifier 或带有 & ref-qualifier 的函数
— "对 cv X 的右值引用",用于使用 &&ref-限定符声明的函数
但是等等,还有更多。
[5] 对于没有 ref 限定符声明的非静态成员函数,附加规则适用:
即使隐式对象参数不是常量限定的,右值也可以绑定到参数,作为 只要在所有其他方面,参数都可以转换为隐式对象参数的类型。
因此
- 您只能重载一种引用类型:左值、右值
- 您不能重载一个或另一个,然后添加另一个不合格的 ref,因为不合格的那个被定义为绑定两种类型,因此存在歧义。
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 为什么两个不同的未命名名称空间可以共存于一个cpp文件中
- 运行同一解决方案的另一个项目的项目
- 对RValue对象调用的LValue ref限定成员函数
- 挂起和取消挂起一个文件DLL
- 用C++中的一个变量定义一个常量
- 函数向量_指针有不同的原型,我可以构建一个吗
- 在c++中用vector填充一个简单的动态数组
- 如何在选项卡视图Qt中设置一个新项目,并保存以前的项目
- 预处理器:插入结构名称中的前一个行号
- 我在c++代码中生成了一个运行时#3异常
- 带有自定义deleter的std::unique_ptr对象的大小(一个由ref捕获的lambda)
- C++将包含的库 (ref DLL) 从根目录更改为另一个位置
- 为什么只允许在一个 ref 限定符上重载
- 我如何使一个模板化的构造函数允许所有的l值ref, r值ref和initializer_list
- 为std::function赋值抽象函数——为什么std::ref是一个解决方案
- 返回一个Ref到一个局部变量
- 用ref. parameter初始化ref. field:是一个副本
- ref类,返回一个标签向量