在引用初始化中使用已删除的复制构造函数进行复制初始化

杰克X:

考虑以下代码:

#include <iostream>
class Data{
public:
    Data() = default;
    Data(Data const&) = delete;
    Data(int) {

    }
};
int main(){
  int a = 0;
  const std::string& rs = "abc"; // rs refers to temporary copy-initialized from char array
  Data const& d_rf = a;          // #2 but here can be complied
  // accroding to the standard, the reference in #2 is bound to a temporary object, the temporary is copy-initialized from the expression
}

[dcl.init.ref]

如果T1或T2是类类型,并且T1与T2不是引用相关的,则使用通过用户定义的转换对类型为“ cv1 T1”的对象进行复制初始化的规则来考虑用户定义的转换([dcl.init ],[over.match.copy],[over.match.conv]);如果相应的非引用副本初始化格式不正确,则程序格式不正确。如非引用副本初始化所述,对转换函数的调用结果将用于直接初始化引用。对于这种直接初始化,不考虑用户定义的转换

复制初始化

否则(例如,对于其余的复制初始化情况),将枚举可以从源类型转换为目标类型或(当使用转换函数时)转换为其派生类的用户定义转换,如[上文所述]。 match.copy],最好的选择是通过重载分辨率([over.match])。如果转换无法完成或模棱两可,则初始化格式错误。所选函数以初始化器表达式作为参数进行调用;如果函数是构造函数,则调用为目标类型的cv不合格版本的prvalue,其结果对象由构造函数初始化。根据上述规则,该调用用于直接初始化作为复制初始化目标的对象。

根据标准,类型aint,初始化的引用的类型为Data,因此from intData使用用户定义的转换对“ cv1 T1”类型的对象进行复制初始化的规则,考虑用户定义的转换这意味着Data const& d_rf = a;可以翻译为Data temporary = a; Data const& d_rf = temporary;对于Data temporary = a;,即使复制省略存在,复制/移动构造函数必须检查其是否可用,但拷贝构造函数class Data已被删除,为什么能不能遵守?

这是来自
参考的标准副本初始化引用的一些引用

从cppreference 复制引用的初始化

如果引用是左值引用:

如果object是一个左值表达式,并且其类型是T或从T派生,并且等于或小于cv限定,则引用将绑定到由左值标识的对象或其基类子对象。
如果object是一个左值表达式,并且其类型可以隐式转换为T或从T派生的类型(相等或更少的cv限定),则源类型及其返回lvalue的基类的非显式转换函数将考虑引用,并通过重载分辨率选择最佳引用。然后,将引用绑定到由转换函数返回的左值标识的对象(或其基类子对象)

否则,如果引用是对const的右值引用或左值引用:

如果object是x或相等或更少cv限定的T或从T派生的xvalue,类prvalue,数组prvalue或函数lvalue类型,则引用将绑定到初始化程序表达式的值或其绑定基础子对象。
如果object是可以隐式转换为x值,类prvalue或T或从T派生的,等于或小于cv限定的类型的函数值的类类型表达式,则将引用绑定到结果转换或其基础子对象。
否则,将构造类型T的临时对象并从object复制初始化。然后将引用绑定到此临时目录。复制初始化规则适用(不考虑显式构造函数)。
[例:
const std :: string&rs =“ abc”; // rs表示从char数组临时复制初始化 ]

更新:

我们考虑N337下的代码

根据标准,值a的类型为int,引用引用的目标类型为Data,因此编译器需要Data通过复制初始化来生成临时类型毫无疑问,这里我们专注于复制初始化源类型为int,目标类型为Data,这种情况符合:

否则(即,对于其余的复制初始化情况),如13.3所述,列举可以从源类型转换为目标类型或(当使用转换功能时)为其派生类的用户定义转换序列。 1.4,最佳选择是通过过载分辨率(13.3)选择的。如果转换无法完成或模棱两可,则初始化格式错误。所选函数以初始化器表达式作为参数进行调用;如果函数是构造函数,则调用将初始化目标类型的cv不合格版本的临时版本。临时变量是一个prvalue。然后,根据上面的规则,调用的结果(对于构造方法来说是临时的)用于直接初始化作为复制初始化目标的对象在某些情况下,允许通过将中间结果直接构造到要初始化的对象中来消除此直接初始化中固有的复制。

注意粗体部分,并不意味着该值通过int即可直接初始化临时项Data::Data(int)它意味着,int首先转变为Data通过Data::Data(int),然后该结果直接初始化临时其是复制初始化的目的地的目的在这里。如果我们使用代码来表示粗体部分,则就像Data temporary(Data(a))

上面的规则在这里:

—如果初始化是直接初始化,或者如果复制是初始化,其中源类型的cv不合格版本与目标的类相同,或为该类的派生类,则考虑构造函数。列举了适用的构造函数(13.3.1.3),并通过重载分辨率(13.3)选择了最佳的构造函数。如此选择的构造函数将以初始化器表达式或expression-list作为其参数来初始化对象。如果没有构造函数适用,或者重载解决方案不明确,则初始化格式错误。

请返回Data temporary(Data(a))显然,复制/移动构造函数是参数Data(a)的最佳匹配。但是,,Data(Data const&) = delete;因此复制/移动构造函数不可用。为什么编译器不报告错误?

xskxzr:

此问题已由问题1604解决,建议的解决方案似乎可以确认此类代码格式错误,因此我将其视为编译器错误。

幸运的是,从C ++ 17开始,此代码由于保证了复制省略变得格式正确,这与编译器一致。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

需要移动构造函数,但未使用,在数组初始化中使用已删除复制构造函数的类元素

使用复制构造函数初始化new []

初始化列表中的复制构造

默认初始化与从默认构造值进行的复制初始化

委托复制构造函数和const数据初始化

复制构造函数是否重新初始化文件成员

为什么对象的初始化调用复制构造函数?

初始化,复制构造函数和赋值

无法在复制构造函数中初始化数组

复制构造函数花括号初始化

非原始成员初始化中的复制构造函数

错误的数组初始化与赋值复制构造函数初始化

删除类的复制构造函数时,初始化类的数组成员

用已删除的副本构造函数初始化const引用成员

删除构造函数后进行初始化

使用函数的返回值初始化对象时未调用复制构造函数

在构造函数初始化器中使用map的初始化器列表

在初始化列表中使用空构造函数初始化父类?

复制初始化:为什么即使关闭复制删除功能也未调用move或copy构造函数?

根据条件通过复制或使用引用来进行 C++ 初始化

在构造函数初始化列表中使用 std::variant

在Java中使用初始化程序与构造函数

使用私有构造函数进行静态初始化

使用委托构造函数进行成员初始化

使用构造函数进行参考初始化

如何使用已删除的副本构造函数初始化类数组(C ++ 11)

使用已删除的副本构造函数初始化抽象类

在C ++中使用隐式转换进行复制初始化

C ++在构造函数中使用表达式初始化引用