【问题标题】:Constructor inheritance and direct member initialisation [duplicate]构造函数继承和直接成员初始化
【发布时间】:2017-04-17 09:38:32
【问题描述】:

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

#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):

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 添加一个默认构造函数:

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

gcc 也可以编译它。代码的行为方式完全相同,特别是添加的 Foo 的默认构造函数永远不会被执行。

所以我现在的问题是,这是有效的 C++ 11 吗?如果是,我可能在 gcc 中发现了一个错误。否则,gcc 和 clang 不应该都给我一条错误消息,指出这不是有效的 C++ 11?

在@vlad-from-moscow 很好地回答了这个问题后进行编辑:这个错误似乎也存在于 gcc 6.2 中,所以我将提交一个错误报告。

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

【问题讨论】:

  • 我在 cppreference.com 等页面上找不到任何相关信息。那里描述了“使用”语法和大括号初始化器,但没有提到任何关于结合两者的内容。

标签: c++ c++11 gcc constructor


【解决方案1】:

gcc 不满足 C++ 标准。 Derived 类的继承构造函数应使用为 Derived 继承构造函数指定的参数调用其 mem-initializer 列表中的 Base 构造函数。

有写在C++标准(12.9继承构造函数)

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

也根据章节(12.6.2 初始化基和成员)

8 在非委托构造函数中,如果给定的非静态数据成员 或基类不是由 mem-initializer-id 指定的(包括 由于构造函数没有 mem-initializer-list 的情况 具有 noctor-initializer) 并且该实体不是 一个抽象类(10.4),然后

——如果实体是一个非静态数据成员,它有一个 大括号或相等初始化器,实体按照中指定的方式初始化 8.5;

【讨论】:

  • @VladfromMoscow:问题不在于Derived(120) 不调用Base(120),更多的是它错误地认为表达式列表将是foo()(这是无效的),即使一个提供了初始化程序,实际调用将是foo(42)。如果您删除 foo 数据成员,则 gcc 接受该代码。
  • @MatthieuM。我已经附加了我的答案。但是我发现标准的各个版本之间存在矛盾。
【解决方案2】:

看来你是对的,gcc 中有一个错误

来自 §12.9 [class.inhctor]:

命名构造函数的using-declaration (7.3.3) 隐式声明了一组继承构造函数。这 using-declaration 中命名的类X 的继承构造函数 的候选集包括实际的 由默认参数转换产生的构造函数和名义构造函数如下:

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

所以这意味着你的Derived 类应该明确地从它的基类中获取一个接受int 的构造函数。遵循类内成员初始化的常规规则,如果没有 Foo 的默认构造函数,构造 Derived 的实例应该不是问题,因为它没有被使用。因此,gcc 中有一个错误:

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

当类类型的对象被直接初始化 (8.5) [...] 时,重载决议选择构造函数。对于直接初始化,候选 函数是被初始化对象的类的所有构造函数。

所以应该选择构造函数Foo::Foo(int),它显然不在gcc中。


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

方便的是,该标准在此摘录下方提供了一个示例(我正在删除不需要的内容):

struct B1 {
   B1(int);
};

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

D1 中的构造函数集是 [Emphasis 我的]

  • D1(),隐式声明的默认构造函数,如果使用 odr,则格式错误
  • D1(const D1&amp;),隐式声明的复制构造函数,不继承
  • D1(D1&amp;&amp;),隐式声明的移动构造函数,不继承
  • D1(int),隐式声明的继承构造函数

【讨论】:

  • 这与Derived或Base的默认构造函数无关。 gcc 需要 Foo 的默认构造函数,但它从未真正调用它。
  • @MartinHierholzer:您说“这段代码使用 clang 以预期的行为编译和执行。使用 gcc 无法编译,因为编译器删除了 Derived: 的构造函数”。这是不正确的 gcc 行为,也是我在这里要解决的问题。你应该不需要再做任何事情了。
  • 参考您的编辑:这仍然与 Derived 的默认构造函数是否被删除无关。我对 Derived 的默认构造函数不感兴趣,它从未使用过,gcc 也从未抱怨没有它。相反,gcc 删除了想要的构造函数 Derived::Derived(int),因为 Foo 的默认构造函数不存在!
  • @MartinHierholzer:为了清晰起见,我进行了更多编辑。我认为我无法提出比这更有力的论据。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多