0
votes

I want to create a template class which has an iterator of a STL container as a member. That is how far I got:

#include <iostream>
#include <vector>

using namespace std;

template<typename Element, template <class> class StdLibContainer>
struct ClassHoldingAnIteratorToAStandardContainer
{
    ClassHoldingAnIteratorToAStandardContainer(){}
    typename StdLibContainer<Element*>::iterator std_lib_iterator;
};


int main()
{
    vector<int> vec{1,2,3};
    ClassHoldingAnIteratorToAStandardContainer<int,vector<int>> holding_iterator_to_vec;
    //DOES NOT WORK, compiler says: expected a class template, got ‘std::vector<int>’
    return 0;
}
  1. Could you explain the syntax template <typename> class StdLibContainer? I found it somewhere on stackoverflow. BUt I don't understand it.

  2. How can I create an instance of ClassHoldingAnIteratorToAStandardContainer ? All my attempts failed so far. The compiler always gives the error message: `expected a class template, got ‘std::vector’

In the above example i want to assign holding_iterator_to_vec vec.begin().

2
That's a template template parameter. It requires a template instead of a type as argument. - François Andrieux
@πάντα ῥεῖ That question has nothing to do with template template parameters. It's one of the cases for typename that isn't mentioned in the answers. - François Andrieux
@FrançoisAndrieux Okeh sorry, I was to fast here. Sounded so at a 1st glance. - πάντα ῥεῖ
@newandlost What's the problem you actually want to solve doing this? It sounds somehow weird, and I have doubts if there are better designs to achieve what you're actually want to do. XY problem? - πάντα ῥεῖ
do you need to know the type of the container? Why not just have the type of the iterator as template parameter? - 463035818_is_not_a_number

2 Answers

1
votes

template <typename> class is the same as template <class> class. Originally, when templates were introduced, they allowed two equivalent forms:

template<class T> struct Foo {};
// or
template<typename T> struct Foo {};

Do not ask me why! However, the same was not true for template template parameters:

template <template <class> typename T> struct Foo {};

was the only allowed syntax. Apparently, people were unhappy about it, so the syntax was relaxed.

As for your second question, std::vector takes at least two template arguments, data type and allocator. This is why a single argument template doesn't cut it before C++17. After C++17, it would work.

To make it universal, use

template<template <class...> class Container> struct Foo{};
0
votes

Unless you really need to know the type of the container, I would strongly recommend to keep your ClassHoldingAnIteratorToAStandardContainer independent of the concrete container type. If you just need the iterator, this is simpler and sufficient:

template<typename iterator>
struct iterator_wrapper {
    iterator iter;    
};

Thats the minimum you need to have an iterator as member :).

I dont really know what you want to use the iterator for, so just for the sake of an example lets add methods that actually use the iterator....

#include <iterator>
#include <vector>
#include <iostream>

template<typename iterator>
struct iterator_wrapper {
    using value_type = typename std::iterator_traits<iterator>::value_type;
    iterator iter;    
    bool operator!=(const iterator& other) { return iter != other;}
    iterator_wrapper& operator++(){ 
        ++iter;
        return *this;
    }
    const value_type& operator*() { return *iter; }
};

template <typename iterator>
iterator_wrapper<iterator> wrap_iterator(iterator it) { 
    return {it}; 
}

int main() {
    std::vector<int> vec{1,2,3};
    auto it = wrap_iterator(vec.begin());    
    for (;it != vec.end();++it) std::cout << *it;       

}

Also there is a problem in your code.

typename StdLibContainer<Element*>::iterator

is for containers of pointers while in main you have ints. If you want to infer the iterator type from the container type then you can do it for example like this:

template <typename container, 
          typename iterator = typename container::iterator>
iterator_wrapper<iterator> wrap_begin(container& c) { 
    return {c.begin()}; 
}

which makes creating an iterator_wrapper as simple as

auto x = wrap_begin(vec);

Note that this answer applies to C++11, in newer standards there are deduction guides that make such make_x methods more or less superfluous afaik.