首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >构造函数继承和直接成员初始化

构造函数继承和直接成员初始化
EN

Stack Overflow用户
提问于 2016-12-02 05:00:21
回答 2查看 1.5K关注 0票数 24

我试图使用C++ 11直接数据成员初始化和“使用”语法继承基类的构造函数的组合。现在使用gcc 5.4.0 (在Ubuntu16.04上),我观察到一个奇怪的错误,如果数据成员类型没有默认构造函数。在查看以下最小示例时,可能最容易理解:

代码语言:javascript
运行
AI代码解释
复制
#include <iostream>

struct Foo {
  Foo(int arg) { std::cout << "Foo::Foo(" << arg << ")" << std::endl; }
};

struct Base {
  Base(int arg) { std::cout << "Base::Base(" << arg << ")" << std::endl; }
};

struct Derived : public Base {
  using Base::Base;
  Foo foo{42};
};

int main() {
  Derived derived{120};
}

此代码使用clang编译和执行预期的行为。对于gcc,它不编译,因为编译器删除构造函数Derived::Derived(int)

代码语言:javascript
运行
AI代码解释
复制
ttt.cpp: In function ‘int main():
ttt.cpp:17:22: error: use of deleted function ‘Derived::Derived(int)’
   Derived derived{120};
                      ^
ttt.cpp:12:15: note: ‘Derived::Derived(int)’ is implicitly deleted because the default definition would be ill-formed:
   using Base::Base;
               ^
ttt.cpp:12:15: error: no matching function for call to ‘Foo::Foo()’
ttt.cpp:4:3: note: candidate: Foo::Foo(int)
   Foo(int arg) { std::cout << "Foo::Foo(" << arg << ")" << std::endl; }
   ^
ttt.cpp:4:3: note:   candidate expects 1 argument, 0 provided
ttt.cpp:3:8: note: candidate: constexpr Foo::Foo(const Foo&)
 struct Foo {
        ^
ttt.cpp:3:8: note:   candidate expects 1 argument, 0 provided
ttt.cpp:3:8: note: candidate: constexpr Foo::Foo(Foo&&)
ttt.cpp:3:8: note:   candidate expects 1 argument, 0 provided

如果我向Foo添加一个默认构造函数,如下所示:

代码语言:javascript
运行
AI代码解释
复制
  Foo() { std::cout << "Foo::Foo()" << std::endl; };

gcc也能把它编出来。代码的行为完全相同,特别是添加的Foo的默认构造函数从未执行。

所以我的问题是,这是有效的C++ 11吗?如果是的话,我可能在gcc身上发现了一个窃听器。否则,gcc和clang不应该给我一个错误信息,这是无效的C++ 11吗?

在这个问题被莫斯科的弗拉德很好地回答后编辑:这个bug似乎也出现在gcc 6.2中,所以我会提交一个bug报告。

第二个编辑:已经有一个bug,我在第一个搜索中没有找到:bug.cgi?id=67054

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-12-02 05:44:06

gcc不符合C++标准。派生类的继承构造函数应该使用为派生继承构造函数指定的参数调用它的mem初始化程序列表中的基构造函数。

在C++标准中编写了(12.9继承构造函数)

8.当类的继承构造函数被用于创建类类型(1.8)的对象时,它被隐式地定义为defined。隐式-de继承构造函数执行类的初始化集合,该类将由用户编写的内联构造函数执行,该类的内联构造函数具有mem初始化程序-列表,其惟一的mem-初始化器-id指定在使用-的嵌套名称-specifier中表示的基类,以及函数体中的复合语句为空。如果用户编写的构造函数格式不正确,则程序的格式不正确。表达式列表中的每个表达式都是形式static_cast( p ),其中p是对应构造函数参数的名称,T是声明的p类型。

也根据这一节(12.6.2初始化基地和成员)

8在非委托构造函数中,如果给定的非静态数据成员或基类不是由mem初始化器id指定的(包括不存在mem初始化程序列表的情况,因为构造函数具有夜行初始化程序),且实体不是抽象类的虚拟基类(10.4),则该实体不是抽象类的虚拟基类(10.4)。 -如果实体是具有大括号或等初始化器的非静态数据成员,则按8.5中指定的方式初始化该实体;

票数 11
EN

Stack Overflow用户

发布于 2016-12-02 05:16:29

看来你是对的,gcc身上有个小毛病

来自第12.9节class.inhctor

一个使用声明(7.3.3),它隐式地声明了一组继承构造函数。在using-声明中命名的类X中继承的构造函数的候选集合包括实际的构造函数和由默认参数转换产生的概念构造函数,如下所示:

  • X的所有非模板构造函数

因此,这意味着您的Derived类应该从其基础上获得一个接受int的构造函数。按照类内成员初始化的常规规则,如果没有Foo的默认构造函数,构造Foo实例不应该是一个问题,因为它没有被使用。因此,gcc身上有一个bug:

§13.3.1.3构造函数over.match.ctor初始化

当类类型的对象直接初始化(8.5) .时,重载解析选择构造函数.对于直接初始化,候选函数都是被初始化的对象类的构造函数。

所以构造函数Foo::Foo(int)应该被选中,显然不是gcc中的构造函数。

在阅读完这篇文章后,我有一个问题是:“这会导致删除Derived的默认构造函数吗?”答案是否定的。

方便地,标准在下面提供了一个示例(我正在删除不需要的内容):

代码语言:javascript
运行
AI代码解释
复制
struct B1 {
   B1(int);
};

struct D1 : B1 {
   using B1::B1;
};

D1中存在的构造函数集是地雷。

  • D1()**,隐式声明的默认构造函数,如果使用odr时格式不正确**
  • D1(const D1&),隐式声明的复制构造函数,而不是继承的
  • D1(D1&&),隐式声明的移动构造函数,而不是继承的
  • D1(int),隐式声明的继承构造函数
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40932844

复制
相关文章
【Kotlin】Kotlin 构造函数 ( 主构造函数 | 主构造函数声明属性 | init 初始化代码块 | 次构造函数 | 构造函数委托 | 调用构造函数创建实例对象 )
1 . 构造函数个数 : Kotlin 类定义时需要指定主构造函数 , 还可以指定 0 ~ 多个次构造函数 ;
韩曙亮
2023/03/27
4.3K0
【C++】构造函数调用规则 ( 默认构造函数 | 默认无参构造函数 | 默认拷贝构造函数 | 构造函数调用规则说明 )
如果 C++ 类中 没有定义构造函数 , C++ 编译器会自动为该类提供一个 " 默认的无参构造函数 " , 函数体为空 , 不做任何操作 ;
韩曙亮
2023/10/15
1.4K0
【C++】构造函数调用规则 ( 默认构造函数 | 默认无参构造函数 | 默认拷贝构造函数 | 构造函数调用规则说明 )
dotnet C# 只创建对象不调用构造函数方法
有时我期望只是创建出对象,但是不要调用对象的构造方法,可以通过使用 FormatterServices 的 GetUninitializedObject 函数来实现只创建对象不调用构造函数方法
林德熙
2021/12/24
8600
java构造函数调用另一个构造函数_java中的构造函数
* 构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象时,其实就是在调用构造方法
用户7886150
2021/04/29
4.7K0
JAVA & .NET创建对象构造函数调用顺序
基类静态初始化块——当前类静态初始化块——基类初始化块——基类构造函数——当前类初始化块——当前类构造函数
雪飞鸿
2019/03/08
1.1K0
虚函数中构造函数的调用顺序
1 /*曾经有段时间一直被构造函数中的虚函数所困扰,现在通过自己重新学习了一遍,标注一下容易忘记的知识*/ 2 #include<iostream> 3 using namespace std; 4 class Base0 5 { 6 public: 7 Base0(int var):var0(var) 8 { 9 cout<<"Construct base0"<<endl; 10 } ; 11 int var0; 12 void fun()
Gxjun
2018/03/22
3.7K0
虚函数中构造函数的调用顺序
【C++】构造函数意义 ( 构造函数显式调用与隐式调用 | 构造函数替代方案 - 初始化函数 | 初始化函数缺陷 | 默认构造函数 )
C++ 提供的 构造函数 和 析构函数 作为 类实例对象的 初始化 和 销毁 方案 ;
韩曙亮
2023/10/15
9360
【C++】构造函数意义 ( 构造函数显式调用与隐式调用 | 构造函数替代方案 - 初始化函数 | 初始化函数缺陷 | 默认构造函数 )
【C++】构造函数分类 ① ( 构造函数分类简介 | 无参构造函数 | 有参构造函数 | 拷贝构造函数 | 代码示例 - 三种类型构造函数定义与调用 )
无参构造函数 : 只负责为指针类型的成员变量分配内存 , 并赋值一个初始值 , 没有具体的赋值信息 , 该初始化只能为 成员变量 赋值一个默认值 ;
韩曙亮
2023/10/15
5270
【C++】构造函数分类 ① ( 构造函数分类简介 | 无参构造函数 | 有参构造函数 | 拷贝构造函数 | 代码示例 - 三种类型构造函数定义与调用 )
java构造函数方法声明无效_如何构造函数
Java构造函数,也叫构造方法,是JAVA中一种特殊的函数。与函数名相同,无返回值。
全栈程序员站长
2022/10/05
1.7K0
C++构造函数 | 构造函数
C++在建立一个对象时,常常需要作某些初始化,如果一个数据成员未被赋值,则它的值是不可预知的,因为在系统为它分配内存时,保留了这些存储单元的原状,这就成为了这些数据成员的初始值,在C++中,对象是一个实体,它反映了客观事物的属性,是应该有确定的值的。
小林C语言
2021/01/26
2.3K0
C++构造函数 | 构造函数
js 中的构造函数,构造函数作用,构造函数和普通函数的区别
函数的定义方式: 1.声明式函数定义: function 函数名 (){};这种定义方式,会将函数声明提升到该函数所在作用域的最开头,也是就无论你在这个函数的最小作用域的那儿使用这种方式声明的函数,在这个作用域内,你都可以调用这个函数为你所用。 2.函数表达式:let fun = function(){}; 此方式定义的函数,只能在该作用域中,这段赋值代码执行之后才能通过fun()调用函数,否则,由于变量声明提升,fun === undefined。 3.new Function 形式: var fun1 = new Function (arg1 , arg2 ,arg3 ,…, argN , body );Function构造函数所有的参数都是字符串类型。除了最后一个参数, 其余的参数都作为生成函数的参数即形参。这里可以没有参数。最后一个参数, 表示的是要创建函数的函数体。
全栈程序员站长
2022/10/04
3.6K0
通过工厂函数、构造函数创建对象
当我们有多个变量的结构非常类似时,如下所示,反复书写结构过于麻烦,我们可以定义一个工厂函数来创建对象
很酷的站长
2022/12/21
8210
通过工厂函数、构造函数创建对象
禁止在构造函数里调用虚函数
在构造函数中调用虚函数会导致程序出现莫名其妙的行为,这主要是对象还没有完全构造完成。下面我们先来看一段代码:
喵叔
2020/09/08
1.7K0
【说站】javascript new如何调用构造函数
以上就是javascript new调用构造函数,希望对大家有所帮助。更多Javascript学习指路:Javascript
很酷的站长
2022/11/23
1.3K0
【说站】javascript new如何调用构造函数
Java构造函数调用顺序问题
今天对Java的构造函数调用顺序进行研究,使用的是与C++类似的方法,即不对源码进行研究,而是直接通过打印代码对构造函数的调用顺序进行研究。
用户7886150
2021/04/20
1.1K0
Java构造函数调用顺序问题
今天对Java的构造函数调用顺序进行研究,使用的是与C++类似的方法,即不对源码进行研究,而是直接通过打印代码对构造函数的调用顺序进行研究。
全栈程序员站长
2022/06/28
7490
Groovy 元组构造函数创建
Groovy 1.8添加了@TupleConstructor注释。 通过这个注释,我们可以在编译时自动创建一个元组构造函数。 因此构造函数可以在编译的类中找到。 对于类中的每个属性,将使用默认值创建构造函数中的参数。 类中定义的属性的顺序还定义了构造函数中参数的顺序。 因为参数具有默认值,所以我们可以使用Groovy语法,并在使用构造函数时将参数留在参数列表的末尾。
白石
2019/09/18
1.3K0
【C++】构造函数与析构函数概念简介 ( 构造函数和析构函数引入 | 构造函数定义与调用 | 析构函数定义与调用 | 代码示例 )
在 C++ 语言中 , 创建对象时 , 需要进行对象创建的初始化工作 , 如 : 创建集合数组 , 为成员变量设置初始值 ;
韩曙亮
2023/10/15
3980
【C++】构造函数与析构函数概念简介 ( 构造函数和析构函数引入 | 构造函数定义与调用 | 析构函数定义与调用 | 代码示例 )
小朋友学C++(10):子类构造函数调用父类构造函数
从哲学层面来看,子类会继承父类除private以外的所有成员。 因为构造函数是公有的,所以理所当然地会被子类继承。 程序1: #include <iostream> using namespace std; class Shape { public: Shape() { cout << "Shape's constructor method is invoked!\n"; } }; class Rectangle : public Shape { public:
海天一树
2018/04/17
1.5K0
【C++】构造函数初始化列表 ⑤ ( 匿名对象 生命周期 | 构造函数 中 不能调用 构造函数 )
下面的代码中 , 在 fun 函数中 , 调用 Student(18, 180) 代码 , 创建 Student 类型的匿名对象 , 匿名对象的生命周期 只存在于 这一行代码 , 该行代码执行完毕后 , 匿名对象 就会被 析构 ;
韩曙亮
2023/10/15
2770
【C++】构造函数初始化列表 ⑤ ( 匿名对象 生命周期 | 构造函数 中 不能调用 构造函数 )

相似问题

C++大父级默认构造函数调用

13

调用多个构造函数--只调用同一个构造函数?

40

从构造函数调用构造函数

24

构造函数调用其他构造函数。

10

从动态创建的子构造函数调用父构造函数

13
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档