Home > OS >  Forward declare using directive for recursive definitions
Forward declare using directive for recursive definitions

Time:07-28

I have an array with items of type variant that I want to iterator over using the generic std::array iterator. Now I want to do the management of the array with my own class array2. However, the variant might also contain an object of type array2 which is instantiated using the variant itself, which means that val is already required as a template parameter.

From a logical standpoint this problem seems solvable: I don't use a data member of type val in array2, this means memory layout of val can be fixed upon definition. However, I would have to forward declare the val-type to use it in the definition of val. Can I do that somehow? Or is there a more idiomatic approach to this problem / a workaround?

I have checked out C Forward declare using directive but it doesn't answer my question because it doesn't use recursion (which makes it a lot easier).

CompilerExplorer

#include <variant>
#include <cstddef>
#include <array>


template <typename T, size_t V> struct array2;

template <size_t V>
using val = std::variant<std::monostate, int, array2<val, V>>;

template <typename T, size_t V>
struct array2
{
    using iterator = typename std::array<T, V>::iterator;

    iterator begin_; // array allocation not done in here
    iterator end_;
};

int main() {}

Yields:

<source>:9:54: error: 'val' was not declared in this scope
    9 | using val = std::variant<std::monostate, int, array2<val, V>>;

Update:

Note that array2's size does not depend on the definition of val, except that the using declaration does require it for std::array<val, V> to be complete apparently.

CodePudding user response:

No, what you want is not possible.

cppreference says about type-alias using:

The type-id [target type] cannot directly or indirectly refer to identifier [type being defined].

It doesn't matter if the problem is "logically" solvable in some scenarios, as—C being statically typed language—all types must be known at compile time, and recursive types simply can't be that because their type resolving process would never end.

Forward declaration is something completely different: with forward declaration you are simply giving type declaration without its definition, but there is no problem with that type itself, and the linker will take care of finding the definition. See this answer for more on forward declarations.

You need to simply get rid of recursion like this, get array2<val, V> out of val. You could use something like this for the same effect:

using val = std::variant<std::monostate, int>;

template <size_t V>
using valOrArray2Val = std::variant<val, array2<val, V>>;

CodePudding user response:

I've found a solution. In case you typedef a class type you can simply create a proxy type that inherits the class and benefit from the fact that template instantiation is only done when the declaration of proxy is already complete:

LiveDemo

#include <variant>
#include <cstddef>
#include <array>


template <typename T, size_t V> struct proxy;

template <typename T, size_t V>
struct proxy : std::variant<std::monostate, int, proxy<T, V>>
{
    using iterator = typename std::array<T, V>::iterator;

    iterator begin_; // array allocation not done in here
    iterator end_;
};

int main() {}

Note: This still only works if there are no proxy data members within the proxy class (but this is obvious in this case).

  • Related