user11244958
user11244958

Reputation:

Creating a template class of a container

Im trying to create a class as container-wrapper, something like this

#include <vector>

template <typename T, typename U>
class Test {
    T<U> list;
};

int main() {
  Test<std::vector, int> test;
}

But this wont work. I know it will work when I use it like this

#include <vector>

template <typename T>
class Test {
    T list;
};

int main() {
  Test<std::vector<int>> test;
}

but how would I write my insert?

template <typename T>
class Test {
    T list;

public:
    void insert(??? element) {
        ...
    }
};

TIA, Narase

Upvotes: 3

Views: 348

Answers (2)

Hiroki
Hiroki

Reputation: 2880

But this wont work.

With your first snippet of Test, since std::vector has two template parameters, value_type and allocator_type, we must specify the existence of these template parameters in the declaration of T using template template parameters as follows. Here I use variadic one and then T<U...> becomes legal for T = std::vector and compilation errors are removed.

how would I write my insert?

The following code also shows an implementation example of Test::insert. Since this method can accept both lvalue and rvalue references in C++11 and over, here I apply the forwarding reference (which is what Scott Meyers calls universal reference,) to it. In this approach, we does not need to specify the value type of Test::list in the Test::insert definition and passing wrong types to Test::insert causes compilation errors. If you need the value type of std::vector, the member type std::vector::value_type is available:

template <template<class...> class T, class ...U>
class Test
{
    T<U...> list;

public:
    template<class ...V>
    void insert(V&& ...element) {
        list.insert(std::forward<V>(element)...);
    }

    typename T<U...>::iterator begin() noexcept {
        return list.begin();
    }

    typename T<U...>::iterator end() noexcept {
        return list.end();
    }

    using value_type = typename T<U...>::value_type;
};

This is an usage example with std::vector:

DEMO (std::vector)

static_assert(
    std::is_same<Test<std::vector, int>::value_type, int>::value, "oops!"); // OK.

Test<std::vector, int> test; // OK

test.insert(test.end(), 11);
test.insert(test.end(), 99);

for(auto it = test.begin(); it != test.end(); ++it){
    std::cout << *it << std::endl;
}

As another example, this wrapper class also works with std::map as follows:

DEMO (std::map)

static_assert(
    std::is_same<Test<std::map, int, int>::value_type, std::pair<const int, int>>::value, "oops!"); // OK.

Test<std::map, int, int> test; // OK

test.insert(std::make_pair(1, 11));
test.insert(std::make_pair(2, 99));

for(auto it = test.begin(); it!= test.end(); ++it){
    std::cout << it->first << ", " << it->second << std::endl;
}

Finally, this is a fixed version of your second snippet:

DEMO (std::vector)

DEMO (std::set)

template <class T>
class Test
{
    T list;

public:
    template<class ...V>
    void insert(V&& ...element) {
        list.insert(std::forward<V>(element)...);
    }

    typename T::iterator begin() noexcept {
        return list.begin();
    }

    typename T::iterator end() noexcept {
        return list.end();
    }

    using value_type = typename T::value_type;
};

Upvotes: 3

n. m. could be an AI
n. m. could be an AI

Reputation: 120079

The easiest one would be

template <typename T>
class Test {
    ....
    void insert(const typename T::value_type& element);

And/or, for completeness and move semantics compliance:

void insert(typename T::value_type&& element);

Upvotes: 3

Related Questions