构造函数继承和直接成员初始化 [英] Constructor inheritance and direct member initialisation

查看:175
本文介绍了构造函数继承和直接成员初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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(){
派生派生{120};
}

这段代码用clang编译并执行期望的行为。使用gcc它不会编译,因为编译器会删除构造函数 Derived :: Derived(int)

  ttt.cpp:在函数'int main()'中:
ttt.cpp:17:22:错误:使用已删除函数'Derived :: Derived(int)'
派生派生{120};
^
ttt.cpp:12:15:注意:'Derived :: Derived(int)'被隐式删除,因为默认定义将会不合格:
使用Base :: Base ;
^
ttt.cpp:12:15:错误:没有匹配函数调用'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:注:候选人需要1个参数,0提供
ttt.cpp:3:8:注意:候选人:constexpr Foo :: Foo(const Foo&)
struct Foo {
^
ttt.cpp:3:8:note:candidate期望1个参数,0提供
ttt.cpp:3:8:note:候选人:constexpr Foo :: Foo(Foo&)
ttt.cpp:3:8:备注: p>

如果我为这样的Foo添加默认构造函数:

  Foo ){std :: cout<< Foo :: Foo()<<的std :: ENDL; }; 

gcc也可以编译它。代码的行为方式完全一样,尤其是Foo的默认构造函数从未得到执行。



现在我的问题是,这个有效的C ++ 11是什么?如果是的话,我可能已经在gcc中发现了一个bug。否则,gcc和clang都不应该给我一个错误消息,说明这是无效的C ++ 11?



编辑问题后,很好地回答了@ vlad-from-莫斯科:这个bug似乎也存在于gcc 6.2中,所以我会提交一个bug报告。



第二编辑:已经有一个bug,我没有在第一个搜索中找到: https://gcc.gnu.org/bugzilla/show_bug .cgi?id = 67054

解决方案

gcc不符合C ++标准。 Derived类的继承构造函数应该在它的mem-initializer列表中调用Base构造函数,并使用为Derived继承构造函数指定的参数。



用C ++标准编写(12.9继承构造函数)


8当一个类的继承构造函数在
被使用时被隐式定义(3.2)创建类类型的对象(1.8)。
隐式定义的继承构造函数执行该类的
初始化设置,该类将由用户编写的
内联构造函数为该类执行,并带有mem-initializer-list其中只有
的mem-initializer具有一个mem-initializer-id,用于命名在using-declaration的嵌套名称指定符中指示的基类
类,以及在下面指定的
表达式列表
,并且其函数体中的
复合语句为空(12.6.2)。如果
用户编写的构造函数不合格,则该程序为
格式不正确。表达式列表中的每个表达式都是
static_cast(p)的形式,其中p是相应的
构造函数参数的名称,T是声明的p类型。


同样根据(12.6.2初始化基础和成员)部分


<在非委托构造函数中,如果给定的非静态数据成员
或基类不是由mem-initializer-id指定的(包括没有mem-initializer的
情况因为构造函数
具有noctor-initializer)并且实体不是
的一个虚拟基类一个抽象类(10.4),那么

- 如果实体是一个非静态数据成员,它有一个
的括号或等于初始值设定项,那么该实体将按照
8.5;

< blockquote>

I am trying to use a combination of the C++ 11 direct data member initialisation and the "using" syntax to inherit the constructors of a base class. Now with gcc 5.4.0 (on Ubuntu 16.04) I observe a strange error, if the data member type has no default constructor. It is probably easiest to understand when looking on the following minimal example:

#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};
}

This code compiles and executes with the expected behaviour with clang. With gcc it does not compile, because the compiler deletes the constructor Derived::Derived(int):

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

If I add a default constructor to Foo like this:

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

also gcc can compile it. The code behaves exactly in the same way, in particular the added default constructor of Foo never gets executed.

So my question is now, is this valid C++ 11? If yes, I probably have found a bug in gcc. Otherwise, shouldn't both gcc and clang give me an error message that this is not valid C++ 11?

Edit after the question was nicely answered by @vlad-from-moscow: This bug seems to be present also in gcc 6.2, so I will file a bug report.

2nd edit: There already is a bug, which I didn't find in the first search: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67054

解决方案

The gcc does not satisfy the C++ Standard. The inherited constructor of the class Derived should call the Base constructor in its mem-initializer list with the argument specified for the Derived inherited constructor.

There is written in the C++ Standard (12.9 Inheriting constructor)

8 An inheriting constructor for a class is implicitly defined when it is odr-used (3.2) to create an object of its class type (1.8). An implicitly-defined inheriting constructor performs the set of initializations of the class that would be performed by a user-written inline constructor for that class with a mem-initializer-list whose only mem-initializer has a mem-initializer-id that names the base class denoted in the nested-name-specifier of the using-declaration and an expression-list as specified below, and where the compound-statement in its function body is empty (12.6.2). If that user-written constructor would be ill-formed, the program is ill-formed. Each expression in the expression-list is of the form static_cast(p), where p is the name of the corresponding constructor parameter and T is the declared type of p.

Also according to the section (12.6.2 Initializing bases and members)

8 In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has noctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then

— if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized as specified in 8.5;

这篇关于构造函数继承和直接成员初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆