Reputation:
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
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
:
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:
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:
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
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