C++ circular reference of classes members

135 views Asked by At

I have the following code (simplified for brevity). I'm making an interpreter and executor of simple "programs", which basically consist of commands and loops.

class Command
{
    int code, arg; // whatever
};

class Loop;
class Statement;

class Scope
{
    std::vector<Statement> statements;
public:
    Statement& appendCmd(const Command& = {}); // works
    Statement& appendLoop(const Loop& = {}); // cannot convert from 'initializer list' to 'const Loop &'
    Statement& appendLoop(const Loop& = Loop()); // use of undefined type 'Loop'
};

class Loop
{
    Scope scope;
    int iters;
};

class Statement
{
    std::variant<std::monostate, Command, Loop> sttmnt;
};

The problem is, class Loop requires definition of class Scope for the member variable, while class Scope requires definition of class Loop to construct the default value of argument. How can I solve this? Also I'd rather not make any of them nested inside any other.

I already had this problem before, but it could be omitted by moving method definitions out of the header file. However default argument value must be in the header...

Edit: Updated the code which hopefully removes the UB of variant having undefined type.

2

There are 2 answers

2
Paul Sanders On BEST ANSWER

I suppose one solution would be:

#include <memory>
#include <variant>
#include <vector>

class Scope;

class Command
{
    int code, arg; // whatever
};

class Loop
{
    std::unique_ptr <Scope> scope;
    int iters;
};

using Statement = std::variant<std::monostate, Command, Loop>;

class Scope
{
    std::vector<Statement> statements;
public:
    Statement& appendCmd(const Command& = {});
    Statement& appendLoop(const Loop& = {});
};

But @RetiredNinja's comment is highly relevant.

0
Weijun Zhou On

Maybe not the best solution in terms of practice, but this is something I have done before.

class Command
{
    int code, arg; // whatever
};

class Loop;
class Statement;
extern const Loop emptyLoop;

class Scope
{
    std::vector<Statement> statements;
public:
    Statement& appendCmd(const Command& = {}); // works
    Statement& appendLoop(const Loop& = emptyLoop);
};

class Loop
{
    Scope scope;
    int iters;
};

class Statement
{
    std::variant<std::monostate, Command, Loop> sttmnt;
};

//In Loop.cpp
const Loop emptyLoop{};

See https://godbolt.org/z/zrex9WE7T for a partial demo.