g++ 中的奇怪编译错误(clang++ 编译良好)

发布于 2024-10-17 18:27:33 字数 9790 浏览 6 评论 0原文

我正在尝试编译一个实例化此类的文件。 GCC 给了我一些神秘的错误,但是 clang 毫无怨言地编译了它。

错误:

statemachine.h: In member function ‘void state_machine<Data, T>::start_submachine(void (*)(state_machine<Data, T>&, T), void (*)(state_machine<Data, T>&, T))’:
statemachine.h:245: error: ‘substate_machine<Data, T>::substate_machine(state_machine<Data, T>*)’ is protected
statemachine.h:215: error: within this context
statemachine.h: In member function ‘state_machine<Data, T>* substate_machine<Data, T>::parent()’:
main.cpp:282:   instantiated from here
statemachine.h:138: error: ‘state_machine<Data, T>* state_machine<Data, T>::parent()’ is protected
statemachine.h:241: error: within this context
statemachine.h: In member function ‘void substate_machine<Data, T>::state_return()’:
main.cpp:282:   instantiated from here
statemachine.h:232: error: ‘void state_machine<Data, T>::return_from_sub()’ is protected
statemachine.h:254: error: within this context

Main.cpp 长 282 行,它指向的行只是一个右大括号 }。 Parent() 永远不会在类之外被调用(那么为什么它会抱怨它受到保护)?为什么它会抱怨 state_return() 调用受保护的方法,因为这是该类的成员。 GCC/G++ 是否会搞砸模板中受保护的数据成员?我怀疑(这只是一种预感)它试图内联扩展宏等函数......但为什么呢?

代码:

#ifndef STATEMACHINE_H_INC
#define STATEMACHINE_H_INC

//#include <iostream> TODO:  Make better templated stream type
#include <memory>
#include <string>

const std::string null_string = "";

/* Class state_machine:
 *      Templated class to allow easy implementation of FSMs.
 * 
 *      HOW TO USE:
 *          - Make a type containing whatever data needs to be passed
 *            to the current state.
 *          - If necessary, create a preprocessor function run before
 *            the actual state is invoked (prefunc)
 *          - Create a function for each state.  In addition, each
 *            state can be made a submachine by using substate_machine
 *          - If necessary, specialize (INLINE and in the HEADER FILE)
 *            the finalize() method.
 *          - The function names should pretty much be self explanitory.
 *      
 *      Hooray for function pointers.  The code was 5x longer and 10x
 *      buggier before I implemented lexer as a state machine :D.
 * 
 *  NOTE:  This class COPY CONSTRUCTS from the hints provided (at least
 *         for now), so *don't* try and use your old pointer- it's not the
 *         same object!  This was done to simplify this class's
 *         implementation.  At some point I should probably change it...
 * 
 */

template <class Data, class T>
class state_machine {
public:
    //public use typedefs
    typedef void (*state)(state_machine<Data, T>&, T);
    typedef state prefunc_t;
    static void defprefunc(state_machine<Data, T>&, T);
    static void defstate(state_machine<Data, T>&, T);
    static void submachine_handle(state_machine<Data, T>&, T);
    //The above works with submachines because references are treated
    //by the standard like pointers- so polymorphism is allowed
private:
    prefunc_t prefunc;
    //don't feel like writing a full on destructor for one pointer
    std::auto_ptr<Data> internal_data;
    state curstate;
    state returnstate;
    //this MUST be an auto_ptr or our memory management gets REAL tricky
    std::auto_ptr < state_machine<Data, T> > substate;
protected:
    void init();
    void call(state_machine<Data, T>&, T);
    //this method allows submachine to get data from top of hierarchy.
    virtual state_machine<Data, T> * parent(); //return TOP of tree
    void return_from_sub(); //this is a slot, to use the qt term
public:
    //public interface
    state_machine(prefunc_t = defprefunc, Data * = NULL);
    Data& data();
    void change_state(state);
    //TODO:  change std::istream to a stream dependent on T
    //void add_stream(std::istream&);
    void add_char(T);
    //NULL here means curstate:
    void start_submachine(state, state = NULL);
    //this method is available for specialization
    void finalize();
    virtual void state_return();
};

/* class substate_machine:
 *      This class is a helper class to allow the creation of state
 *      machines as states within another state machine.  Submachines:
 *          -Share the same data.
 *          -Behave exactly like a regular state, except upon exiting
 *           the submachine the state should call the state_return()
 *           method, which allows control to flow to the parent machine.
 *          -Are invoked with the start_submachine() method.
 *      Basically, what allows them to share data is the protected
 *      virtual method parent(), which gets the state_machine object
 *      at the hierarchy's root.  This is never used by the submachine,
 *      only in the parent machine methods when accessing shared data
 *      (i.e. the subclass provides 'plug-in' functionality with this
 *      method), so it *could* be made a private virtual, but those seem
 *      to be 1. poorly understood and 2. overprotective in cases like
 *      this (i.e. do we *really* care if the submachine knows how to
 *      access its parent? no, in fact, we encourage it).
 * 
 *      The user should never see this class.  It is only to be used
 *      by the state_machine parent class provide transparent operation
 *      of substates (don't you love polymorphism>)
 */

template <class Data, class T>
class substate_machine : public state_machine<Data, T> {
    state_machine<Data, T> * parentsm; //direct parent state machine
    substate_machine() {} //Default construction causes failure
protected:
    virtual state_machine<Data, T> * parent();
    substate_machine(state_machine<Data, T>*);
public:
    virtual void state_return(); //send a signal to the parent machine
};

// definitions

//note that state_machine<Data, T>::parent() returns the TOP of the
//hierarchy, NOT the direct parent.

template <class Data, class T>
state_machine<Data, T> * state_machine<Data, T>::parent() {
    return this; //base class state machine must be at top of hierarchy
}

template <class Data, class T>
void state_machine<Data, T>::finalize() {
    //this is left to be <intentionally> specialized over
}

template <class Data, class T>
Data& state_machine<Data, T>::data() {
    //use parent here to allow all subs to access the hierarchy's shared
    //data as if they own it.
    return *(parent()->internal_data);
}

//these are two different functions for clarity's sake

template <class Data, class T>
void state_machine<Data, T>::defstate
(state_machine<Data, T>& self, T c) {
    //do nothing - default behavior
}

template <class Data, class T>
void state_machine<Data, T>::defprefunc
(state_machine<Data, T>& self, T c) {
    //do nothing - default behavior
}

template <class Data, class T>
void state_machine<Data, T>::
submachine_handle(state_machine<Data, T>& self, T c) {
    //handle a submachine
    self.substate->curstate(*(self.substate), c);
}

template <class Data, class T>
void state_machine<Data, T>::state_return() {
    //should NOT happen, but handle just in case.
}

template <class Data, class T>
void state_machine<Data, T>::init() {
    curstate = defstate;
    prefunc = defprefunc;
}

template <class Data, class T>
state_machine<Data, T>::state_machine
(prefunc_t func, Data * d) {
    init();
    //make a new data - copy construct if d is not null
    if (d) {
        internal_data = std::auto_ptr<Data>(new Data(*d));
    }
    else {
        internal_data = std::auto_ptr<Data>(new Data);
    }
    prefunc = func;
}

template <class Data, class T>
void state_machine<Data, T>::change_state(state s) {
    curstate = s;
}

//the first state is the state to start a submachine in, the second
//state is the state to go into when the submachine returns to the
//parent, which is by default NULL (the current state)
template <class Data, class T>
void state_machine<Data, T>::start_submachine(state s, state rs) {
    //get arround default argument errors (static resolution...)
    if (rs == NULL) {
        rs = curstate;
    }
    //set up submachines
    substate = std::auto_ptr<state_machine<Data, T> >(new substate_machine<Data, T>(this));
    substate->change_state(s);
    returnstate = rs;
    //set up the submachine state handler
    curstate = submachine_handle;
}

//preprocess and then process a character through the state machine.
template <class Data, class T>
void state_machine<Data, T>::add_char(T c) {
    prefunc(*this, c);
    curstate(*this, c);
}

//this is a slot for the submachine to send its return signal to.
//basically just switches the function pointer back.
template <class Data, class T>
void state_machine<Data, T>::return_from_sub() {
    curstate = returnstate;
}

//now for the substate

template <class Data, class T>
state_machine<Data, T> * substate_machine<Data, T>::parent() {
    //remember, this is the top of the hierarchy.
    return parentsm->parent();
}

template <class Data, class T>
substate_machine<Data, T>::
substate_machine(state_machine<Data, T> * sm) {
    this->init();
    parentsm = sm;
    //initialization.  MUST BE INITIALIZED BY A PARENT THROUGH THIS CTOR
}

template <class Data, class T>
void substate_machine<Data, T>::state_return() {
    parentsm->return_from_sub();
    //sends the parent the return signal.
}

#endif

提前感谢您的任何输入。我会用 clang++ 标记,但它不会让我......

I'm attempting to compile a file that instantiates this class. GCC gives me cryptic errors, but clang compiles it without a complaint.

Errors:

statemachine.h: In member function ‘void state_machine<Data, T>::start_submachine(void (*)(state_machine<Data, T>&, T), void (*)(state_machine<Data, T>&, T))’:
statemachine.h:245: error: ‘substate_machine<Data, T>::substate_machine(state_machine<Data, T>*)’ is protected
statemachine.h:215: error: within this context
statemachine.h: In member function ‘state_machine<Data, T>* substate_machine<Data, T>::parent()’:
main.cpp:282:   instantiated from here
statemachine.h:138: error: ‘state_machine<Data, T>* state_machine<Data, T>::parent()’ is protected
statemachine.h:241: error: within this context
statemachine.h: In member function ‘void substate_machine<Data, T>::state_return()’:
main.cpp:282:   instantiated from here
statemachine.h:232: error: ‘void state_machine<Data, T>::return_from_sub()’ is protected
statemachine.h:254: error: within this context

Main.cpp is 282 lines long, the line that it's pointing to is just a closing brace }. Parent() is never called outside the class (so why would it complain about it being protected)? And why would it complain about state_return() calling a protected method, as this is a member of the class. Does GCC/G++ screw up with protected data members in templates? I suspect (and this is just a hunch) it's trying to inline expand the functions like macros... but why?

The code:

#ifndef STATEMACHINE_H_INC
#define STATEMACHINE_H_INC

//#include <iostream> TODO:  Make better templated stream type
#include <memory>
#include <string>

const std::string null_string = "";

/* Class state_machine:
 *      Templated class to allow easy implementation of FSMs.
 * 
 *      HOW TO USE:
 *          - Make a type containing whatever data needs to be passed
 *            to the current state.
 *          - If necessary, create a preprocessor function run before
 *            the actual state is invoked (prefunc)
 *          - Create a function for each state.  In addition, each
 *            state can be made a submachine by using substate_machine
 *          - If necessary, specialize (INLINE and in the HEADER FILE)
 *            the finalize() method.
 *          - The function names should pretty much be self explanitory.
 *      
 *      Hooray for function pointers.  The code was 5x longer and 10x
 *      buggier before I implemented lexer as a state machine :D.
 * 
 *  NOTE:  This class COPY CONSTRUCTS from the hints provided (at least
 *         for now), so *don't* try and use your old pointer- it's not the
 *         same object!  This was done to simplify this class's
 *         implementation.  At some point I should probably change it...
 * 
 */

template <class Data, class T>
class state_machine {
public:
    //public use typedefs
    typedef void (*state)(state_machine<Data, T>&, T);
    typedef state prefunc_t;
    static void defprefunc(state_machine<Data, T>&, T);
    static void defstate(state_machine<Data, T>&, T);
    static void submachine_handle(state_machine<Data, T>&, T);
    //The above works with submachines because references are treated
    //by the standard like pointers- so polymorphism is allowed
private:
    prefunc_t prefunc;
    //don't feel like writing a full on destructor for one pointer
    std::auto_ptr<Data> internal_data;
    state curstate;
    state returnstate;
    //this MUST be an auto_ptr or our memory management gets REAL tricky
    std::auto_ptr < state_machine<Data, T> > substate;
protected:
    void init();
    void call(state_machine<Data, T>&, T);
    //this method allows submachine to get data from top of hierarchy.
    virtual state_machine<Data, T> * parent(); //return TOP of tree
    void return_from_sub(); //this is a slot, to use the qt term
public:
    //public interface
    state_machine(prefunc_t = defprefunc, Data * = NULL);
    Data& data();
    void change_state(state);
    //TODO:  change std::istream to a stream dependent on T
    //void add_stream(std::istream&);
    void add_char(T);
    //NULL here means curstate:
    void start_submachine(state, state = NULL);
    //this method is available for specialization
    void finalize();
    virtual void state_return();
};

/* class substate_machine:
 *      This class is a helper class to allow the creation of state
 *      machines as states within another state machine.  Submachines:
 *          -Share the same data.
 *          -Behave exactly like a regular state, except upon exiting
 *           the submachine the state should call the state_return()
 *           method, which allows control to flow to the parent machine.
 *          -Are invoked with the start_submachine() method.
 *      Basically, what allows them to share data is the protected
 *      virtual method parent(), which gets the state_machine object
 *      at the hierarchy's root.  This is never used by the submachine,
 *      only in the parent machine methods when accessing shared data
 *      (i.e. the subclass provides 'plug-in' functionality with this
 *      method), so it *could* be made a private virtual, but those seem
 *      to be 1. poorly understood and 2. overprotective in cases like
 *      this (i.e. do we *really* care if the submachine knows how to
 *      access its parent? no, in fact, we encourage it).
 * 
 *      The user should never see this class.  It is only to be used
 *      by the state_machine parent class provide transparent operation
 *      of substates (don't you love polymorphism>)
 */

template <class Data, class T>
class substate_machine : public state_machine<Data, T> {
    state_machine<Data, T> * parentsm; //direct parent state machine
    substate_machine() {} //Default construction causes failure
protected:
    virtual state_machine<Data, T> * parent();
    substate_machine(state_machine<Data, T>*);
public:
    virtual void state_return(); //send a signal to the parent machine
};

// definitions

//note that state_machine<Data, T>::parent() returns the TOP of the
//hierarchy, NOT the direct parent.

template <class Data, class T>
state_machine<Data, T> * state_machine<Data, T>::parent() {
    return this; //base class state machine must be at top of hierarchy
}

template <class Data, class T>
void state_machine<Data, T>::finalize() {
    //this is left to be <intentionally> specialized over
}

template <class Data, class T>
Data& state_machine<Data, T>::data() {
    //use parent here to allow all subs to access the hierarchy's shared
    //data as if they own it.
    return *(parent()->internal_data);
}

//these are two different functions for clarity's sake

template <class Data, class T>
void state_machine<Data, T>::defstate
(state_machine<Data, T>& self, T c) {
    //do nothing - default behavior
}

template <class Data, class T>
void state_machine<Data, T>::defprefunc
(state_machine<Data, T>& self, T c) {
    //do nothing - default behavior
}

template <class Data, class T>
void state_machine<Data, T>::
submachine_handle(state_machine<Data, T>& self, T c) {
    //handle a submachine
    self.substate->curstate(*(self.substate), c);
}

template <class Data, class T>
void state_machine<Data, T>::state_return() {
    //should NOT happen, but handle just in case.
}

template <class Data, class T>
void state_machine<Data, T>::init() {
    curstate = defstate;
    prefunc = defprefunc;
}

template <class Data, class T>
state_machine<Data, T>::state_machine
(prefunc_t func, Data * d) {
    init();
    //make a new data - copy construct if d is not null
    if (d) {
        internal_data = std::auto_ptr<Data>(new Data(*d));
    }
    else {
        internal_data = std::auto_ptr<Data>(new Data);
    }
    prefunc = func;
}

template <class Data, class T>
void state_machine<Data, T>::change_state(state s) {
    curstate = s;
}

//the first state is the state to start a submachine in, the second
//state is the state to go into when the submachine returns to the
//parent, which is by default NULL (the current state)
template <class Data, class T>
void state_machine<Data, T>::start_submachine(state s, state rs) {
    //get arround default argument errors (static resolution...)
    if (rs == NULL) {
        rs = curstate;
    }
    //set up submachines
    substate = std::auto_ptr<state_machine<Data, T> >(new substate_machine<Data, T>(this));
    substate->change_state(s);
    returnstate = rs;
    //set up the submachine state handler
    curstate = submachine_handle;
}

//preprocess and then process a character through the state machine.
template <class Data, class T>
void state_machine<Data, T>::add_char(T c) {
    prefunc(*this, c);
    curstate(*this, c);
}

//this is a slot for the submachine to send its return signal to.
//basically just switches the function pointer back.
template <class Data, class T>
void state_machine<Data, T>::return_from_sub() {
    curstate = returnstate;
}

//now for the substate

template <class Data, class T>
state_machine<Data, T> * substate_machine<Data, T>::parent() {
    //remember, this is the top of the hierarchy.
    return parentsm->parent();
}

template <class Data, class T>
substate_machine<Data, T>::
substate_machine(state_machine<Data, T> * sm) {
    this->init();
    parentsm = sm;
    //initialization.  MUST BE INITIALIZED BY A PARENT THROUGH THIS CTOR
}

template <class Data, class T>
void substate_machine<Data, T>::state_return() {
    parentsm->return_from_sub();
    //sends the parent the return signal.
}

#endif

Thanks in advance for any input. I would tag with clang++ but it won't let me...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

软糯酥胸 2024-10-24 18:27:33

正如编译器所说,这些东西都受到了保护。父类不能调用派生类的受保护构造函数(反之亦然)。

class A
{
protected:
    A(int) {}
public:
    void foo();
};

class B: public A
{
protected:
    B(int):
        A(10) //OK here
     {}

};

void A::foo()
{
    B b(10); //error, that constructor is not accessible to A
}

并且 substate_machine 的父方法确实试图通过静态类型不是 substate_machine 的指针来调用受保护的方法。

class A
{
protected:
    void foo() {}
};

class B: public A
{
    void bar() {
        this->foo(); //OK
        B other_b;
        other_b.foo(); //OK
        A a;
        a.foo(); //not OK
        A* b_ptr = &other_b;
        b_ptr->foo(); //not OK, static type of *b_ptr is not B
    }
};

我想知道您是否期望 protected 意味着“只要两个类属于同一继承树,任何类都可以访问任何其他类的受保护部分”?...

Those things are protected alright, as the compiler says. A parent class cannot invoke the protected constructor of a derived class (it works the other way).

class A
{
protected:
    A(int) {}
public:
    void foo();
};

class B: public A
{
protected:
    B(int):
        A(10) //OK here
     {}

};

void A::foo()
{
    B b(10); //error, that constructor is not accessible to A
}

And substate_machine's parent method is indeed trying to invoke a protected method through a pointer whose static type is not a substate_machine.

class A
{
protected:
    void foo() {}
};

class B: public A
{
    void bar() {
        this->foo(); //OK
        B other_b;
        other_b.foo(); //OK
        A a;
        a.foo(); //not OK
        A* b_ptr = &other_b;
        b_ptr->foo(); //not OK, static type of *b_ptr is not B
    }
};

I wonder if you expect protected to mean "any class can access any other class's protected parts as long as both classes belong to the same inheritance tree"?...

一抹微笑 2024-10-24 18:27:33

我相信 g++ 是正确的。看起来您正在从另一个模板调用一个模板的受保护初始化构造函数。它属于不同的类别,因此不可用。

I believe g++ is correct. It looks like you're calling the protected initializing constructor of one template from the other template. It's in a different class and so isn't available.

缱绻入梦 2024-10-24 18:27:33

这看起来像是 g++ 中的一个错误。它不应该检查 substate_machine 构造函数的访问,因为它具有依赖类型。包含 new-expression 的函数可能仅针对 substate_machine 专门化且具有 public 构造函数的类型进行实例化,因此编译器不允许拒绝此代码。

我找不到有此错误的 g++ 版本;您使用的是哪个版本?

This looks like a bug in g++. It should not be checking the access of substate_machine<Data, T>'s constructor because it has a dependent type. The function containing the new-expression might only be instantiated for types where substate_machine is specialized and has a public constructor, so the compiler is not permitted to reject this code.

I cannot find a version of g++ that has this bug; which version are you using?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文