考虑以下代码:
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;
}
你会同意
int**
转换为int*const*
,并且int *const*&b (a)
是直接初始化。首先,我们参考n4700中的11.6.3,第5段[dcl.init.ref]。
对类型“ 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
和收益类型“左值引用 cv2T2
”或“ cv2T2
”或“右值参考”中分别为 cv2T2
“,其中T2
与T
的类型相同,或者可以转换为T
类型资格转换也是候选函数。
在此处,S::operator int**&
会产生"左值T2 (= int**)
",并且可以通过资格转换转换为T (= int*const*)
。在这里,我们可以说转换是可能的,但g ++ - 7和clang-5.0都不接受该程序。那是为什么?
答案 0 :(得分:7)
我们正在寻找的参考初始化规则是[dcl.init.ref]:
对类型“ cv1
T1
”的引用由“ cv2T2
”类型的表达式初始化,如下所示:
我们 cv1 T1
为int* const*
, cv2 T2
为S
。然后我们仔细阅读下一节:
如果引用是左值引用和初始化表达式
- 是左值(但不是位字段),“cv1 T1”与“cv2 T2”引用兼容,或
- 有一个类类型(即T2是一个类类型),其中T1与T2无参考相关,可以转换为左值类型为“cv3 T3”,其中“cv1 T1”与“cv3 T3”引用兼容(通过枚举适用的转换函数([over.match.ref])并通过重载分辨率选择最佳转换函数来选择此转换),< / LI>
然后引用绑定到第一种情况下的初始化表达式lvalue和第二种情况下转换的左值结果(或者,在任何一种情况下,绑定到对象的相应基类子对象)。
我们的参考是左值参考。初始化表达式是左值,但这两种类型不是reference-compatible,因此第一个项目符号不适用。
初始化表达式确实具有非引用相关的类类型,但不能将其转换为引用兼容类型。参考兼容部分很重要。 int**
与<{1}} 引用兼容,虽然前者可以转换为后者,但结果不是左值 - 这也是必需的。
因此,本节不适用,我们move on。
否则,引用应该是对非易失性const类型的左值引用(即,cv1应为const),或者引用应为右值引用。
我们的参考符合这两个标准,因此初始化是不正确的。
这种失败的简单版本是:
int* const*
当我们对非const类型的左值引用时,我们无法进行限定转换。