Rob W.
Rob W.

Reputation: 362

Variadic templated container passing reference to contained items

I'm trying to create a variadic templated container that will contain items that have a reference back to the container. Unfortunately I can't quite figure out how to declare the container. It's a bit of a chicken and egg problem. The Items are templated on the Container, but the Container is also templated on the Items.

I've tried to distill down the relevant code below. It complains that "CollectionA" isn't declared.

How can I make this work?

#include <tuple>

template <typename CollectionT, typename Derived>
class ItemBase
{
public:
    ItemBase(CollectionT& c) : collection(c) {}

protected:
    CollectionT& collection;
};

template <typename CollectionT>
class ItemA : public ItemBase<CollectionT, ItemA<CollectionT> > {
    void aMethod() {
        this->collection.doSomething();
    }
};

template <typename CollectionT>
class ItemB : public ItemBase<CollectionT, ItemB<CollectionT> > {

};

template <typename ...Items>
class Collection
{
public:
    Collection() : items(*this) {}

    std::tuple<Items...> items;

    void doSomething() {}
};

int main( int, char** )
{
    // The Items are templated on the Collection, so how do I specify them?
    using CollectionA = Collection<ItemA<CollectionA>, ItemB<CollectionA>>;
    CollectionA collection;
}

Upvotes: 1

Views: 60

Answers (2)

RedFog
RedFog

Reputation: 1015

if you confirm all of Items has CollectionA itself as the template argument, why not pass the template?

template <template<typename>class... ItemTempls>
class Collection{
public:
    Collection() : items(*this) {}

    std::tuple<ItemTempls<Collection>...> items;

    void doSomething() {}
};

int main( int, char** ){
    using CollectionA = Collection<ItemA, ItemB>;
    CollectionA collection;
}

similarly to CRTP, template template parameter is always used to solve the problem of recursive declaration.

Upvotes: 1

Ranoiaetep
Ranoiaetep

Reputation: 6637

This should do the job.

#include <tuple>

class CollectionBase
{
public:
    void doSomething() {}
};

class ItemBase
{
public:
    ItemBase(CollectionBase& c) : collection(c) {}

protected:
    CollectionBase& collection;
};

template <typename ...Items>
class Collection : CollectionBase
{
public:
    Collection() : items{Items{*this}...} {}

    std::tuple<Items...> items;
};

template <typename T>
class Item : ItemBase
{
public:
    Item(CollectionBase& c) : ItemBase(c) {}

    void doSomething()
    {
        collection.doSomething();
    }
};

int main( int, char** )
{
    Collection<Item<int>, Item<char>> collection;
}

Upvotes: 0

Related Questions