5

我是一个老 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;

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

4

2 回答 2

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;

您收到构造错误是因为您的子状态没有声明构造函数,因此它们获得了编译器提供的默认值,并且没有来自同级或基类引用(编译器不提供s2(s1&)or s2(state&))。

你得到了错误的超状态,C因为C{B}调用了默认的复制构造函数s2(s2&),它在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;

当 M 的构造函数运行时,首先是它的基类(没有基类),然后它的成员使用指定的初始化按声明顺序构造。只有一个: B(A),所以其余的都是默认的。在构造完所有基和成员之后,对象构造函数的主体就会运行。

于 2012-05-20T23:04:45.393 回答
3
  struct SuperstateA : state {
  } superstateA;

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

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

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

另一方面,superstateAandsubstateB1都可以转换为state &(或者它们的指针可以转换为state *),因此提供SubstateB::SubstateB( state & )会很好地修补所有漏洞。

于 2012-05-20T22:39:22.730 回答