b1s*_*sub 5 c++ language-lawyer c++17
考虑以下代码:
int **p = 0;
class S {
public:
explicit operator int**&() {
return p;
}
};
int main() {
S a;
int *const*&b (a); // error in both g++-7 and clang-5.0 with "-std=c++17"
return 0;
}
Run Code Online (Sandbox Code Playgroud)
你会同意的
int**到int*const*是可能的,并int *const*&b (a)是直接初始化.首先,我们指的是11.6.3条第5款[dcl.init.ref]从N4700.
参考输入" CV1
T1 (= int*const*)"是通过类型"的表达初始化CV2T2 (= S)"如下:
- 如果引用是左值引用和初始化表达式
- ...
- 有一个类类型(即,
T2是类类型),其中T1与引用无关T2,并且可以转换为类型为" cv3T3" 的左值,其中" cv1T1"与" cv3T3" 引用兼容(此转换为选择通过枚举适用转换函数(16.3.1.6) ,并选择通过重载解析最好的一个(16.3)),那么引用绑定到第一种情况下的初始化表达式lvalue和第二种情况下转换的左值结果 ......
在这里,我们希望T3是int*const*.如上所述,是否可能的转换是根据16.3.1.6第1段[over.match.ref]确定的.
...假设"引用cv1
T"是要初始化的引用的类型,并且" cvS"是初始化表达式的类型,具有S类类型,候选函数选择如下:
- ...对于直接初始化,那些未隐藏的显式转换函数
S和yield类型分别为"对cv2的 lvalue引用T2"或" cv2T2"或"对cv2的 rvalue引用T2",其中T2类型为T或可以转换T具有资格转换的类型也是候选函数.
这里,S::operator int**&产生"左值引用T2 (= int**)",它可以T (= int*const*)通过限定转换转换.在这里,我们可以说转换是可能的,但g ++ - 7和clang-5.0都不接受该程序.这是为什么?
我们正在寻找的参考初始化规则是[dcl.init.ref]:
参考输入" CV1
T1"是通过类型"的表达初始化CV2T2"如下:
我们有cv1 T1 as int* const*和cv2 T2 as S.然后我们仔细阅读下一节:
如果引用是左值引用和初始化表达式
- 是左值(但不是位域),"cv1 T1"与"cv2 T2"引用兼容,或者
- 具有类类型(即,T2是类类型),其中T1与T2不是引用相关的,并且可以转换为类型为"cv3 T3" 的左值,其中"cv1 T1"与"cv3"引用兼容T3"(通过枚举适用的转换函数([over.match.ref])并通过重载决策选择最佳转换函数来选择此转换),
然后,引用绑定到第一种情况下的初始化表达式lvalue和第二种情况下转换的左值结果(或者,在任何一种情况下,绑定到对象的相应基类子对象).
我们的参考是左值参考.初始化表达式是左值,但这两种类型不是引用兼容的,因此第一个项目符号不适用.
初始化表达式确实具有非引用相关的类类型,但不能将其转换为引用兼容类型.参考兼容部分很重要.int**是不参考兼容int* const*,虽然前者可转化为后者,则结果将不会是一个左值-这也是必需的.
因此,本节不适用,我们继续前进.
否则,引用应是对非易失性const类型的左值引用(即,cv1应为const),或者引用应为右值引用.
我们的参考符合这两个标准,因此初始化是不正确的.
这种失败的一个更简单的版本是:
int* pi;
int const*& r = pi; // error
Run Code Online (Sandbox Code Playgroud)
当我们对非const类型的左值引用时,我们无法进行限定转换.