Self-/circular reference in "using" statements

Viewed 120

I've tried defining a variant type for dynamic variables in my program, but I can't seem to be able to make it store a function that returns it as a type.

using Value = std::variant<Integer, Float, Function>;
using Function = std::function<Value()>;

This won't compile because Function needs to be defined at the time Value is, but Function depends on Value too. I've tried fixing this by inlining the Function type into the variant template list but it seems that using statements can't reference themselves or be forward declared.

My best solution so far has been defining Function as a struct so I can forward declare it. This works, but seems so hacky, so I'm wondering if there's a better way?

struct Function;
// define Value
struct Function : std::function<Value()> {};

To clarify, std::function was used as part of the example because I thought it would be easier to show what I was trying to do and it also was needed for my hacky solution. I'd prefer a way to have this working with plain function pointers too, if possible.

1 Answers

This can't work, because what you are essentially trying to create is this:

std::variant<Integer, Float, std::function<std::variant<Integer, Float, std::function<std::variant<Integer, Float, std::function<std::variant<Integer, Float, std::function<std::variant<Integer, Float, std::function<std::variant<Integer, Float, std::function<std::variant<Integer, Float, std::function<std::variant<Integer, Float, std::function<std::variant<Integer, Float, std::function<std::variant<Integer, Float, std::function<std::variant<Integer, Float, std::function<...()>>()>>()>>()>>()>>()>>()>>()>>()>>()>>()>>;

So basically you have an infinite recursion within the type declarations.

You need to work around this by using some type that doesn't have a recursive definition, but using a forwarded struct directly like you suggest is not an option, because std::variant only allows complete types.

What you could do is forward declare your struct, and then use a container/wrapper/smart pointer of your choice (e.g. std::unique_ptr<Function>) in your std::variant:

struct Function;
using Value = std::variant<Integer, Float, std::unique_ptr<Function>>;
struct Function : std::function<Value()> {};
Related