Reputation: 274
I'm currently having trouble with stack
messing up one of my templated classes.
The problem being that methods like pusk_back
, begin
or end
aren't usable with that type.
So my goal here is to prevent contain from being instantiated with stack<T>
or to add a condition preventing a stack
instance to get to those parts of the code.
Here is what the class
looks like:
template<typename T, template <typename...> typename U>
class contain {
public:
contain() : _container()
{
}
~contain()
{
}
void push(T const&data)
{
this->_container.push_back(data);
}
void aff()
{
std::for_each(_container.begin(), _container.end(),
[](T &var) {::aff(var);});
}
void add()
{
std::for_each(_container.begin(), _container.end(),
[](T &var) {::add(var);});
}
private:
U<T> _container;
};
I tried to specialize the template but wasn't able to find the syntax nor was I able to check the type of the variable at runtime.
Upvotes: 1
Views: 478
Reputation: 20141
I must admit that template
s still appear a little bit like black magic to me. I've successfully used template specializations in various situations (if I had no better idea) but never with template arguments. So, I tried...
When I tried on coliru (compiler g++ (GCC) 8.1.0
) I started with -std=c++11
and soon got some terrible errors. For my luck, there was also a hint to switch to -std=c++17
. I did and things become much better immediately. I remembered roughly that the template template arguments are not well supported in C++11. A short glance on cppreference
proved me right:
template < parameter-list > typename (C++17) | class name(optional) (1)
template < parameter-list > typename (C++17) | class name(optional) = default (2)
template < parameter-list > typename (C++17) | class ... name(optional) (3) (since C++11)1) A template template parameter with an optional name.
2) A template template parameter with an optional name and a default.
3) A template template parameter pack with an optional name.
Having this clarified, I took part of OPs sample code and added a partial specialization for the template
where I
std::stack
.Actually, this is not much different to how templates with type or value arguments are specialized.
cppreference
has an article for this partial template specialization but using the term as search key word there should be many hits for books and tutorials. (I remember the book I once learned C++ templates from mentioned this also – of course.)
So, here my small sample to demonstrate this:
#include <iostream>
#include <stack>
#include <vector>
template<typename T, template <typename...> typename U>
class ContainerT {
private:
U<T> _container;
public:
ContainerT(): _container() { }
~ContainerT() = default;
ContainerT(const ContainerT&) = default;
ContainerT& operator=(const ContainerT&) = default;
bool empty() const { return _container.empty(); }
void push(const T &data) { _container.push_back(data); }
T pop()
{
const T data = _container.back();
_container.pop_back();
return data;
}
};
template<typename T>
class ContainerT<T, std::stack> {
private:
std::stack<T> _container;
public:
ContainerT(): _container() { }
~ContainerT() = default;
ContainerT(const ContainerT&) = default;
ContainerT& operator=(const ContainerT&) = default;
bool empty() const { return _container.empty(); }
void push(const T &data) { _container.push(data); }
T pop()
{
const T data = _container.top();
_container.pop();
return data;
}
};
#define DEBUG(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__
int main()
{
// for std::vector
DEBUG(ContainerT<int, std::vector> vec);
DEBUG(vec.push(1); vec.push(2); vec.push(3));
DEBUG(while (!vec.empty()) std::cout << vec.pop() << '\n');
// for std::stack
DEBUG(ContainerT<int, std::stack> stk);
DEBUG(stk.push(1); stk.push(2); stk.push(3));
DEBUG(while (!stk.empty()) std::cout << stk.pop() << '\n');
// done
return 0;
}
It's obvious that ContainerT
is instanced for ContainerT<int, std::vector>
. Otherwise, the push()
method couldn't compile.
For the ContainerT<int, std::stack>
, the specialization is used instead. (Again, otherwise, the push()
method couldn't compile.)
Output:
ContainerT<int, std::vector> vec;
vec.push(1); vec.push(2); vec.push(3);
while (!vec.empty()) std::cout << vec.pop() << '\n';
3
2
1
ContainerT<int, std::stack> stk;
stk.push(1); stk.push(2); stk.push(3);
while (!stk.empty()) std::cout << stk.pop() << '\n';
3
2
1
Upvotes: 1