当前位置: 首页 > 知识库问答 >
问题:

显式复制构造函数和统一初始化

杜炫明
2023-03-14

显式复制构造函数不允许类似于Foo foo=bar;的东西,并将复制使用强制为Foo foo(bar);。此外,显式复制构造函数也不允许从函数中按值返回对象。然而,我尝试用大括号替换复制初始化,就像这样

struct Foo
{
    Foo() = default;
    explicit Foo(const Foo&) = default;
};

int main()
{
    Foo bar;
    Foo foo{bar}; // error here
}

我得到的错误(g 5.2)

错误:没有匹配的函数用于调用'Foo::Foo(Foo

或(叮当声)

错误:结构初始值设定项中的元素过多

删除显式使代码在g下可编译,但clang仍然会失败,并出现相同的错误(感谢@stophen)。这是怎么回事?统一初始化是否被视为初始值设定项列表构造函数(优于所有其他构造函数)?但是如果是这样的话,为什么当复制构造函数是非显式的时程序会编译呢?

共有1个答案

邵飞鸿
2023-03-14

您遇到了一个案例,在C 14定稿后,核心问题1467的解决方案立即解决了这个问题。

让我们首先注意类foo是一个聚合。您的代码正在为foo执行直接列表初始化。列表初始化规则见[8.5.4p3]。

在C 14中(引用N4140,最接近已发布标准的工作草案),上述段落以:

T类型的对象或引用的列表初始化定义如下:

  • 如果T是聚合,则执行聚合初始化(8.5.1)

[...]

因此,如果类是聚合,编译器将尝试进行聚合初始化,但失败。

这被认为是一个问题,并在工作草案中予以解决。引用当前版本N4527,上述段落现在以:

T类型的对象或引用的列表初始化定义如下:

  • 如果T是类类型,且初始值设定项列表有一个cvU类型的单个元素,其中UT或从T派生的类,从该元素初始化对象(通过复制初始化进行复制列表初始化,或通过直接初始化进行直接列表初始化)
  • 否则,如果T是一个字符数组,并且初始值设定项列表中有一个元素是一个适当类型的字符串文字(8.5.2),则按照该节中的说明执行初始化
  • 否则,如果T是聚合,则执行聚合初始化(8.5.1)

[...]

您的示例现在属于第一个要点描述的情况,并且foo是使用默认的复制构造函数直接初始化的列表(无论它是否是显式的,因为它是直接初始化)。

那就是。。。如果编译器实现缺陷报告中的解决方案。

  • GCC 5.2.0(和6.0.0主干)似乎是这样做的,但似乎有一个与显式相关的bug

更新:自本答复编写以来,工作草案已通过与问题中的示例和上述解释相关的两种方式进行了进一步修订:

  • CWG 2137指出,上面引用的段落中的第一个项目符号将该异常应用于所有类类型(问题注释包含一个相关示例),这有点过火。现在,项目符号的开头是:
    • 如果T是聚合类,[…]

    这并没有改变这样一个事实,即在所有更改都实现后,问题中的示例旨在工作,无论有没有显式的;值得知道的是,使其工作的底层机制发生了轻微的变化。

    请注意,所有这些更改都是缺陷报告的解决方案,因此当编译器处于C 14和C 11模式时,它们也应该适用。

 类似资料:
  • 在G++中。如果复制构造函数不是显式的,代码可以编译并正常工作(但我希望强制执行只有对对象的引用才能用作参数和返回值)。代码还在删除对的调用后进行编译(因此不成问题)。因此,我的问题是,std::sort在调用使编译此代码失败的比较函数时会做什么,以及如何修复它。 经过大量的研究,唯一接近我的问题是复制初始化,对复制构造函数的调用是显式的还是隐式的?它链接到GCC中的一个bug。然而,clang显

  • 上面引号中加粗的表示对复制构造函数的调用是显式的,对吗?是G++错了还是我对标准的解释错了?

  • 映射对构造函数没有限制或要求 (__init__ )类的方法。您可以自由地为您想要的函数要求任何参数,为ORM未知的实例分配属性,并且通常在编写Python类的构造函数时做您通常会做的任何其他事情。 sqlAlchemy ORM不调用 __init__ 从数据库行重新创建对象时。ORM的过程有点类似于Python标准库的 pickle 模块,调用低级 __new__ 方法,然后在实例上悄悄地恢复属

  • 我正在构建一个Javafx gui应用程序,我正面临这个问题。我的程序有多个场景,所以我有不同的FXML文件和控制器类。问题是,在第一个“主菜单”(这是我的应用程序中弹出的第一个)场景中,在控制器的构造函数中,我调用了一些从数据库加载数据的重型方法等等。所以会发生什么。在接下来的场景中我确实有“主菜单按钮”,即切换到主菜单场景!所以每次我回到“主菜单”场景时,构造函数都会调用大量加载数据的方法。但

  • 所以我正在学习构造函数初始值设定项列表,我写了以下代码: 为此我使用了g编译器。它调用的是构造函数而不是复制构造函数。它应该调用复制构造函数,因为我正在创建一个对象来创建另一个对象?这里的问题是什么,标准对此怎么说?

  • 考虑以下代码: 以下是一些引用的标准 拷贝初始化 从cppreference复制引用的初始化 如果object是lvalue表达式,其类型是T或从T派生的,且具有相等或较少CV限定性,则该引用绑定到由lvalue标识的对象或其基类子对象。 如果object是lvalue表达式,其类型隐式转换为T或从T派生的类型,具有相等或较少CV限定性,则考虑源类型及其基类返回lvalue引用的非显式转换函数,并