C ++ 11非静态数据成员统一初始化失败,指针指向同一基类的其他类

时间:2012-05-20 21:10:53

标签: c++ c++11

我是一个老C-dude,试图通过将我的旧状态机框架从C移植到C ++ 11来学习C ++ 11。我的想法是为状态机本身创建一个类,然后为其中的状态设置嵌套类。状态可以是分层的,即超级和子状态。框架需要知道一个状态的超状态,为此我在嵌套状态类中有一个指针(state *superstate)。

我的问题是我打算通过直接在机器类中使用构造函数来设置超级指针,这应该可以在C ++ 11中使用非静态数据成员初始化,通过使用统一初始化。但是在设置为另一种类型的状态/类时,它无法编译(substateB3{superstateA})。但是如果我稍后通过使用特定的函数(set_superstate)为此目的设置它,它具有与构造函数相同的参数,它可以正常工作!如果我将超状态设置为相同类型的状态/类(substateB2{substateB1}),那么接受构造函数就足够了。

我正在使用gcc 4.7.0(以获得对非静态数据成员初始值设定项的支持),这是我的代码:

// My state-machine framework (simplified)
struct machine {
  struct state {
    state()                  : superstate(nullptr) { }     // No superstate => toplevel state!
    state(state &superstate) : superstate(&superstate) { }
    state *superstate;
    void set_superstate(state &superstate) { this->superstate = &superstate; } // Non-ctor way to set superstate
  };
};

// An example of a specific state-machine using my framework
struct Machine : machine {
  struct SuperstateA : state {
  } superstateA;

  struct SubstateB : state {
  } substateB1,              // Compiles OK; gets its superstate set in Machine's ctor below
    substateB2{substateB1},  // Compiles OK; but not correct superstate
    substateB3{superstateA}; // gcc 4.7.0 error: could not convert ‘{((Machine*)this)->Machine::superstateA}’ from ‘<brace-enclosed initializer list>’ to ‘Machine::SubstateB’

  Machine()  { substateB1.set_superstate(superstateA); } // Compiles OK;
} myMachine;

非常感谢任何提示或指导! :)

2 个答案:

答案 0 :(得分:5)

剥离一层继承:

struct m {
    struct state { state *s;
        state() : s(0) {};
        state(state &s) : s(&s) {}
        set(state &s) { this->s = &s; }
    };

    struct s1 : state {} A;   // calls s1(), the default constructor
    struct s2 : state {}    B     // calls s2(), ditto
    ,                       C{B}  // calls s2(const s2&), the default copy constructor
    ,                       D{A}; // calls s2(const s1&)

    m() { B.set(A); } // The m() body runs after all the members are constructed.
} M;

你得到了构造错误,因为你的子语句没有声明构造函数,所以他们得到编译器提供的默认值,并且兄弟或基类引用没有(编译器不提供{{1 }或s2(s1&))。

s2(state&)因为C调用默认的复制构造函数C{B}而在s2(s2&)的正文之前运行。

这就是你想要的:

m()

当M的构造函数运行时,首先是它的基类(没有),然后使用指定的初始化以声明顺序构造其成员。只有一个:struct m { struct state { state *s; state() : s(0) {} state(state &s) : s(&s) {} }; struct s1 : state {} A; // default-constructs, fine struct s2 : state { s2(state &s) : state(s) {} s2(s2&s) : state(s) {} } B // default-constructs , C{B} // calls s2(s2&), invokes state(state&) , D{A}; // calls s2(state&) ; m() : B(A) {}; } M; ,所以其余的都是默认的。在构造完所有基础和成员之后,对象构造函数的主体就会运行。

答案 1 :(得分:3)

  struct SuperstateA : state {
  } superstateA;

  struct SubstateB : state {
  } substateB1, 
    substateB3{superstateA};

SuperstateASubstateB之间没有关系允许在它们之间进行转换或切片。即使SuperstateA是基类(通常与“超级”相关联),SubstateB仍然会有更多成员(作为子类),并且无法从对象中初始化它们。不同的类型。

这不是编译器中的错误,也不是C ++的任意限制。执行这种初始化根本不可能。需要有“是一种”的关系。

另一方面,superstateAsubstateB1都可以投放到state &(或者他们的指针可以转换为state *),因此提供SubstateB::SubstateB( state & )将很好地修补所有的洞。