为什么这个参考绑定格式不正确?

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*) "是通过类型"的表达初始化CV2 T2 (= S) "如下:

  • 如果引用是左值引用和初始化表达式
    • ...
    • 有一个类类型(即,T2是类类型),其中T1与引用无关T2,并且可以转换为类型为" cv3 T3 " 的左值,其中" cv1 T1 "与" cv3 T3 " 引用兼容(此转换为选择通过枚举适用转换函数(16.3.1.6) ,并选择通过重载解析最好的一个(16.3)),

那么引用绑定到第一种情况下的初始化表达式lvalue和第二种情况下转换的左值结果 ......

在这里,我们希望T3int*const*.如上所述,是否可能的转换是根据16.3.1.6第1段[over.match.ref]确定的.

...假设"引用cv1 T "是要初始化的引用的类型,并且" cv S "是初始化表达式的类型,具有S类类型,候选函数选择如下:

  • ...对于直接初始化,那些未隐藏的显式转换函数S和yield类型分别为"对cv2的 lvalue引用T2"或" cv2 T2 "或"对cv2的 rvalue引用T2",其中T2类型为T 或可以转换T具有资格转换的类型也是候选函数.

这里,S::operator int**&产生"左值引用T2 (= int**)",它可以T (= int*const*)通过限定转换转换.在这里,我们可以说转换是可能的,但g ++ - 7和clang-5.0都不接受该程序.这是为什么?

Bar*_*rry 7

我们正在寻找的参考初始化规则是[dcl.init.ref]:

参考输入" CV1 T1 "是通过类型"的表达初始化CV2 T2 "如下:

我们有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类型的左值引用时,我们无法进行限定转换.