Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Circular dependency with full type information

Tags:

c++

templates

I have 2 classes, which are both included in variant. The variant is then used in both classes.

template <typename T>
struct Deferred
{
    typedef T value_type;
};

template <typename T>
struct DeferredContainer
{
    typedef typename T::value_type value_type;
};

class DictionaryObject;
class MixedArrayObject;

using DictionaryObjectPtr = Deferred < DictionaryObject >; // DeferredContainer<DictionaryObject>
using MixedArrayObjectPtr = Deferred < MixedArrayObject >; // DeferredContainer<MixedArrayObject>

typedef boost::variant <
    MixedArrayObjectPtr,
    DictionaryObjectPtr
> ContainerPtr;

class MixedArrayObject
{
public:
    typedef int value_type;

    inline void SetContainer(ContainerPtr obj) { _container = obj; }
    inline ContainerPtr GetContainer() const { return _container; }

private:
    ContainerPtr _container;
};

class DictionaryObject
{
public:
    typedef float value_type;

    inline void SetContainer(ContainerPtr obj) { _container = obj; }
    inline ContainerPtr GetContainer() const { return _container; }

private:
    ContainerPtr _container;
};

I don't know exactly how this works, but when I try to expose some properties on either array or dictionary via typedef (and use them in DeferredContainer), I receive error:

error C2602: 'DeferredContainer::value_type' is not a member of a base class of 'DeferredContainer'

Can I make this work somehow? Any other ideas?


Update 31.5, 4:09 PM.

I have reduced the problem to its minimum. Hope this finally gets the attention. The code.

After you switch the MixedArrayObjectPtr to be DeferredContainer, the code won't compile anymore.

like image 239
Gotcha Avatar asked Aug 24 '26 12:08

Gotcha


1 Answers

I would propose just reordering your code to make sure that all the definitions are in the order that they are used in. We'll have to use Object* instead of Object due to the need of variant to have complete types:

// main types
class Dictionary;
class Array;
template <typename T> struct DeferredContainer;

using DictionaryPtr = DeferredContainer<Dictionary>;
using ArrayPtr = DeferredContainer<Array>;

struct Object;

// now definitions of them
class Dictionary
{
public:
     typedef int value_type;
     std::map<Name, Object*> list; // note Object*
};

class Array
{
public:
     typedef int value_type;
     std::vector<Object*> list; // note only one type here
};

template <typename T>
struct DeferredContainer
{
    // now can do stuff with T::value_type
};

And after all of that, we can declare Object. We cannot forward-declare an alias unfortunately, but we can just wrap up the variant:

struct Object {
    boost::variant<
        DictionaryPtr,
        ArrayPtr
    > value;
};

With that ordering, everything compiles.

like image 182
Barry Avatar answered Aug 26 '26 23:08

Barry



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!