【问题标题】:C++ - Is there a workaround to delegating constructors being not the only members in an initialization listC++ - 是否有一种解决方法来委派构造函数不是初始化列表中的唯一成员
【发布时间】:2020-06-21 21:00:10
【问题描述】:
class Foo
{
public:
  Foo() {}
  Foo(int i) 
   : Foo(), m_bar(0) {}

private:
  int m_bar;
};

是否有使此类代码有效的解决方法?

如果我们有一个委托构造函数,有没有办法在初始化列表中拥有多个成员?为什么要限制委托构造函数是初始化列表中的唯一成员。

【问题讨论】:

  • 编译器应该将m_bar初始化成什么?从他的角度来看,这两个陈述都是冲突的,因为一个变量只能被初始化一次(例如,如果你没有看到问题,请声明 m_bar const)。但是,您可以在 ctor 的主体中为 m_bar 分配另一个值。请注意,这样的分配将在 m_bar 初始化之后发生。
  • 虽然当然可以为这种情况添加优先规则到语言中,但我推测它被故意遗漏了,因为这些事情增加了复杂性(从语言和用户的角度来看)确实增加了任何显着的好处。
  • @SebastianHoffmann 这怎么能与非内联 ctor 一起工作?
  • @curiousguy 好点;当两个 ctor 都在不同的翻译单元中实现时,事情变得不平凡

标签: c++ constructor initialization language-lawyer member


【解决方案1】:

你引用的规则在[class.base.init]/6中定义:

一个 mem-initializer-list 可以委托给另一个构造函数 构造函数的类使用任何表示 构造函数的类本身。如果 mem-initializer-id 指定 构造函数的类,它应该是唯一的内存初始化器;这 构造函数是一个委托构造函数,并且构造函数被选中 mem-initializer 是目标构造函数。目标 构造函数由重载决议选择。一旦目标 构造函数返回,委托构造函数的主体是 执行。如果构造函数直接或间接委托给自己, 该程序格式错误,无需诊断。

假设委托构造函数可以与其他内存初始化器一起使用:

  • 您可以调用两个不同的委托。但只能有一种构造。所以你会在选择哪一个方面发生冲突。
  • 在委托之后使用 mem-initializers,当委托完成其工作时,委托构造函数的执行将不再使用主体而是在剩余的 mem-initializers 中恢复。
  • 但内存初始化器与一个赋值可以覆盖另一个赋值不同。 Mem-initializers 调用成员的构造函数。并且成员变量只能构造一次。然后会在如何初始化列表中的成员以及丢弃哪个构造函数方面发生冲突。
  • 这更糟,因为委托和委托构造函数都必须完全构造一个类对象,即它的所有成员。

这里有一个例子来说明一个只能初始化一次的成员的问题,以及解决方案:

class Foo
{
  const int m_bar;              // can be initalized only once 
  int m_zoo;                    // must be constructed (default possible) but can be overwritten
public:
  Foo(): m_bar(1), m_zoo(2) {}  // the constant can never be changed
  Foo(int i) 
   : Foo() {m_zoo=i;}           // you can still change in the body already constructed items
  Foo(int i, int j)            // comprehensive init 
    : m_bar(i), m_zoo(j) {}    // (has all that delegating may want(
  Foo(char a) 
    : Foo(a,2) {};             // delegate to the comprehensive ctor
};

Online demo

委托规则旨在使这一切保持简单。这只是意味着

  • 只能有一个构造函数来构造对象及其成员(构造函数本身或委托)
  • 委托构造函数只决定委托的参数,并且可以在构造函数主体中添加额外的行为
  • (而且我们都知道只有在构造完所有基类和成员后才调用主体)

因此,使您的设计有效的方法是将所有 mem-initilizer 移动到目标构造函数,并在需要时使用此目标构造函数的参数为​​其 mem-initializers 指定值。或者在正文中覆盖已经初始化的内容;但这并不总是可能的。

【讨论】:

    【解决方案2】:

    问题是因为构造函数中的初始化列表必须初始化每个基类和成员(按声明顺序)具有它自己的构造函数(默认构造函数,如果没有另外指定),: Foo() 必须已经这样做了,并且调用有些成员构造函数两次会很糟糕。

    我想你可能会争辩说,对于可能未初始化的原语,它可以被允许,但在这种情况下,在函数体中分配它们是等价的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-14
      • 2015-02-24
      • 1970-01-01
      • 2016-12-22
      • 1970-01-01
      • 1970-01-01
      • 2019-01-02
      相关资源
      最近更新 更多