c++ - 关于由 volatile 限定符限定的成员函数的问题
问题描述
#include <iostream>
struct A{
A() = default;
A(volatile const A&){}
void show()const volatile {
}
};
int main(){
volatile A a;
//A b = std::move(a); // ill-formed
std::move(a).show(); //OK
}
考虑这个例子,这个例子的结果超出了我对一些相关规则的理解。
对于A b = std::move(a);
,它的格式不正确,因为它违反了以下规则,即:
dcl.init.ref#5.2
否则,如果引用是对非 const-qualified 或volatile-qualified类型的左值引用,则程序格式错误。
这意味着,对 const volatile-qualified T 的左值引用不能绑定到任何右值,即使它们是引用兼容的。 A b = std::move(a);
显然违反了这条规则,因此它的格式不正确。
但是我不知道为什么编译std::move(a).show();
而不报告错误。根据这个规则:
对于非静态成员函数,隐式对象参数的类型是
“对 cv X 的左值引用”对于没有引用限定符或使用 & 引用限定符声明的函数
成员函数的隐式对象参数的类型show
将是volatile const A&
. 一般来说,它肯定违反了 [dcl.init.ref#5.2]。如果将成员函数的定义更改show
为:
void show() volatile const& {
}
std::move(a).show();
将是畸形的。所以在下面的规则中必须有一些魔法,std::move(a).show();
在改变之前编译show
。规则是:
over.match.funcs#general-5
对于没有 ref-qualifier 声明的非静态成员函数,附加规则适用:
即使隐式对象参数不是 const 限定的,也可以将右值绑定到参数,只要在所有其他方面参数可以转换为隐式对象参数的类型。
老实说,我真的不知道“在所有其他方面”这个词是什么意思?而“隐式对象参数的类型”指的是什么?“类型”是指volatile const A&
还是引用的类型volatile const A
?措辞非常模糊。无论如何,对 const volatile T 的左值引用不能绑定到任何类型的右值T
。那么,如何解读呢?
作为对比:
#include <iostream>
struct B{
void show(){}
};
int main(){
volatile B b;
std::move(b).show(); //ill-formed
}
的隐式对象参数的类型show
将是B&
,根据 [over.match.funcs#general-5],即使 ignore const-qualifier
,它仍然是不正确的,因为它丢弃了volatile-qualifier
. 从这个例子可以看出,对于这句话“在所有其他方面,参数都可以转换为隐式对象参数的类型”,其中类型应该引用引用类型而不是引用引用的类型。如果魔力是这样的话,还不足以让其std::move(a).show();
形成良好的形态。
那么,如何解读这些问题呢?我不知道如何使用 [over.match.funcs#general-5] 来解释这两个示例。
解决方案
struct A { A() = default; A(volatile const A &) {} void show() const volatile {} }; int main() { volatile A a; std::move(a).show(); // OK }
根据[over.match.funcs]/4,成员函数的隐含对象参数show()
是,这样,为了重载解决方案,我们可以根据[over.match.funcs]/5考虑数据成员函数为const volatile A&
void show(const volatile A&);
现在,考虑到这一点,让我们首先简化示例,目的是:
- 比较为什么右值引用
A
看似可能绑定到隐含的对象参数或类型const volatile A&
,但当参数用于常规自由函数时,不能说相同类型的函数参数。
因此,请考虑以下简化示例:
#include <memory>
struct A {
void show() const volatile {}
};
void g(const volatile A &) { }
int main() {
volatile A a;
g(std::move(a)); // (i) Error.
std::move(a).show(); // (ii) OK.
}
GCC (10.1.0) 中 (i) 处的错误消息是:
错误:无法将类型的非 const 左值引用绑定到 {aka }类型
const volatile A&
的右值std::remove_reference<volatile A&>::type
volatile A
根据[dcl.init.ref]/5.2 ,这是预期的(正如您自己指出的那样),它不允许右值在初始化时绑定到volatile
引用,即使它们是- 限定的const
。
那么为什么(ii)被接受?或者反过来说,为什么 [dcl.init.ref]/5.2 的限制显然不适用于成员函数的隐式对象参数的类似情况?
答案在于 [over.match.funcs ]/5.1,其中包含声明的成员函数的异常,但没有使用 ref-qualifier:
[over.match.funcs ]/5 [...] 对于没有引用限定符声明的非静态成员函数,适用附加规则:
- /5.1即使隐式对象参数不是 const 限定的,也可以将右值绑定到参数,只要在所有其他方面参数可以转换为隐式对象参数的类型。
[over.match.funcs ]/5.1 取消了 [dcl.init.ref]/5 关于右值(右值绑定)的禁令,剩下的标准适用于参数(右值被忽略;volatile A
)是否可以(“在所有其他尊重" ) 转换为隐式对象参数 ( const volatile A&
)。作为隐式对象参数,如上所示,在这种情况下始终是左值引用,“在所有其他方面”这里本质上意味着隐式对象参数是引用兼容的(根据[dcl.init.ref]/4)使用(忽略右值)参数类型。
// [over.match.funcs ]/5.1 special case: rvalue prohibition waived
volatile A a; // argument: a
const volatile A& aref = a; // ok, reference-compatible
// ^^^^^^^^^^^^^^^^^ implicit object parameter
可以说,[over.match.funcs]/5.1 可以更清楚地说明它既适用于非const
限定(通常)禁止从右值绑定的情况,也适用于volatile
-cv-qualification(通常)禁止从右值绑定的情况。
&
我们最终可以通过显式添加-ref-qualifier来查询编译器这是否实际上是它用于允许 (ii) 的特定规则,根据[over.match.funcs]/4.1将具有的更改对隐式对象参数的类型没有影响:
#include <memory>
struct A {
void show() const volatile & {}
};
void g(const volatile A &) { }
int main() {
volatile A a;
g(std::move(a)); // (i) Error.
std::move(a).show(); // (ii') Error.
}
正如预期的那样,如果我们&
在重载中添加 - 限定符show()
,(ii)同样会像(i)一样失败,尽管有另一个错误消息(GCC):
错误:将
std::remove_reference<volatile A&>::type
{akavolatile A
} 作为this
参数传递会丢弃限定符
对于这个错误,Clang (10.0.0) 可以说有一个更现场的错误消息:
错误:
this
成员函数的参数show
是右值,但函数具有非 const 左值引用限定符
推荐阅读
- c - 使用 void 指针在结构之间创建链接
- python-3.x - 永久更改 matplotlib 中的边距?
- cordova - 蓝牙串行离子不能在 Android 设备上工作,但在 IOS 上工作
- python - 如何放入列表理解
- python - 在 django live 应用程序中将 mysql 数据库从“utf8”更改为“utf8mb4”对数据的影响
- react-native - 如何在 React Native 中为元素添加过渡动画
- java - 几次repaint()后如何重绘JComponent?
- python - python记录使用文件名和行号
- sql - 选择具有优先级值的非重复行
- javascript - 如何将包含 \n 的字符串作为新行添加到 html?