Can you help me understand this C++ template code? - c++

Hya,
Can anyone please tell me how this thing is working?
template <typename T,
template <typename ELEM> class CONT = std::deque >
class Stack {
private:
CONT<T> elems; // elements
public:
void push(T const&); // push element
void pop(); // pop element
T top() const; // return top element
bool empty() const { // return whether the stack is empty
return elems.empty();
}
};
What i don't understand is this :
template class V or say this "template class CONT = std::deque"
i visualize this as
template <class>
class CONT = std::deque // here CONT is templatized class declaration.
but what pesters me is , how can we assign something to class name CONT , rather than writing its definition (which i've done till this time):
template <class>
class CONT{
//def
}
one more thing :
template <class> // why its only class written in angle bracket there should be also be name
like : template<class ty>
Thanks a lot , any help is very appreciated)

What i don't understand is this : template class V
There is no such line in your question, so I can't help with that.
template< template <typename ELEM> class CONT = std::deque >
class Stack
This is a declaration of a template template parameter. You pass a template into the Stack template, and then Stack can use it internally.
The = std::deque part is a default value, in case you leave the CONT parameter unspecified. (std::deque is a predefined template.)
However, this will not work, because std::deque takes two arguments. This will work:
template< template <typename ELEM, typename ALLOC> class CONT = std::deque >
class Stack
However ELEM and ALLOC do not actually name anything; they exist merely to clarify what the parameter list of the required template is. So, you can omit them:
template< template <typename, typename> class CONT = std::deque >
class Stack

It's not an object assignment. It's just syntax in a template specifier to specify what the default type argument should be if one is not provided. It's not a definition for that type.

Related

Passing values to inner class via template arguments C++

Trying to pass the value to nested class via template agrument,and have some confusion:
template<typename A,typename B>
class outter{
int a;
T* p;
////...////
template<typename N=int> class inner;
inner do_something(){
return inner<a>(p)
}
}
template<typename T,typename A, typename N=int>
class outter<T,A>::inner<N>{
sz=N;
}
Task is to create an iterator for vector with range control.
For now im stuck in inner class template declaration and passing arguments. Please, i need advance if that possible. Thanks!
For the inner class, you have template <typename N>, which means that your N is a type, not a value, hence, you cannot assign it to a value.
To solve your issue, try having template <int N> class inner; and also fix the same way at the bottom template<typename T, typename A, int N>
Edit
Just noticed, that at the bottom you have the inner class definition. What you want this sz to be? is it a value or type? In case you want this to be a value, you should write
template<typename T,typename A, int N> // this last int determines also the type to be used down below v
class outter<T,A>::inner<N>{
int sz=N; // this int comes from the upper side ^
}
If you want the sz to be a type defining the sizes within the inner class, then keep the upper and bottom parts to be <..., typename N = int> and put using before sz=N;
template<typename T,typename A, typename N=int>
class outter<T,A>::inner<N>{
using sz=N;
}
If you want to have N type and value be specified through templates, please follow this example:
template </*outer template params*/>
class outer {
template <typename SIZE_TYPE, SIZE_TYPE VALUE = SIZE_TYPE()>
class inner;
};
template </*outer template params*/, typename SIZE_TYPE, SIZE_TYPE VALUE = SIZE_TYPE()>
class outer</*params*/>::inner<SIZE_TYPE, VALUE> {
using size_type = SIZE_TYPE;
size_type my_value = VALUE;
}
Edit 2
In case you want to understand what happens here, please read more about the template instantiation.

Member function of class with template arguments and default arguments outside class

I want to define function outside the template class as described below.
Already tried a lot of combinations for the second argument which is a template and takes default argument as well.
template <typename T>
class CustomAllocator
{
//My custom allocator
};
template <typename T, typename Allocator = CustomAllocator<T> >
class CustomContainer
{
void push_back();
};
/*I want to define push_back outside my class, tried everything.
Almost 4 hours spent through stackoverflow, fluentcpp and all sites*/
// What should be specified for Allocator here ?
template <typename T>
void CustomContainer<T,Allocator>::push_back(T value)
{
}
//OR
template <typename T>
void CustomContainer<T,CustomAllocator<> >::push_back(T value)
{
}
I expect it to be defined outside class
Actual getting compiler error, if it is simple type I could easily mention int,float etc. in the second argument.
Outside of your class definition, it will be unclear to a function what type Allocator is, so you have to redeclare it just like you redeclared T
template <class T, class Allocator>
void CustomContainer<T,Allocator>::push_back(T value)
{
// ...
}
(I'm assuming that DataType should be T)
Note that your declaration of push_back, in the class should match the definition:
template <typename T, typename Allocator = CustomAllocator<T> >
class CustomContainer
{
void push_back(T);
};
You may not use default template arguments for a member function of a template defined outside the template definition.
From the C++ 17 Standard (17.1 Template parameters)
... A default template-argument shall not be specified in the template-
parameter-lists of the definition of a member of a class
template that appears outside of the member’s class.
So just write
template <typename T, typename Allocator>
void CustomContainer<T, Allocator>::push_back( const T &value )
{
//...
}
Pay attention to the argument of the function. Your declaration of the function does not correspond to its definition.

Template template parameter with default values unknown

I have a template function that takes a container as a parameter
(I use this function for both vector, set, map so trying to avoid it would cost a lot of code copying)
so naturally I declared it as:
template<template<class T, class Allocator = std::allocator<T>> class
Container> Container<std::weak_ptr<A>>* Foo() {...}
Note that the return value of Foo uses the default parameter of the container.
When I use the function with vector, list or set it works just fine;
the problem I have is when I try to use this template with a map container with the comparator and value type fixed (which is defined as MapToValue<ValueType>::FromKey with A and Comparator<Key> already defined):
template<class Value> class MapToValue {
template<class Key, class Allocator = std::allocator<std::pair<const
Key, Value>>> FromKey : public std::map<Key, Value, Comparator<Key>,
Allocator> {...}
In this case the default value of the allocator is diffrent then the one in the definition of Foo - std::allocator<std::pair<_Ty, std::string>> vs. std::allocator<_Ty>.
Long story short, I need that to send to Foo a container with a second parameter that can be defaulted WITHOUT knowing what this default type would be (so this function template can be used for both map, vector or basically any other container). Is this possible?
EDIT: I cannot use C++11 in any way, the compiler is gcc 4.1.2 (out of my control)
In c++11 you can take any template as template argument:
template<template <class ...> class Container>
Container<std::weak_ptr<A>>* Foo() {...}
There's a misunderstanding here of how template template parameters operate. This declaration:
template<template<class T, class Allocator = std::allocator<T>> class Container>
Container<std::weak_ptr<A>>* Foo() {...}
Is just an overly verbose version of this declaration:
template < template <class, class> class Container>
Container<std::weak_ptr<A>>* Foo() {...}
(What is A btw?)
It doesn't matter what the names or defaults of the types are that Container takes - Foo is simply templated on some class template that takes two template type arguments. That works with vector because it is a class template that takes two template type arguments:
template<
class T,
class Allocator = std::allocator<T>
> class vector;
It does not work with map because that one takes four template type arguments:
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
> class map;
In C++11, you can generalize your function to have a template template argument take an arbitrary number of template type arguments:
template < template <class...> class Container>
Container<std::weak_ptr<A>>* Foo() {...}
Though, however, this is never going to work as some container types can't be constructed with only a single template argument (e.g. map).
I found a solution, it isn't elegant but it works:
Sending to Foo another template argument that would be the way to deafault the Allocator:
template<template<typename> class DefaultAllocator, template<typename T,
typename Allocator = DefaultAllocator<T>> class Container>
Container<std::weak_ptr<A>>* Foo() {...}
This way I send Foo<std::allocator,std::vector> all the same.
Also by creating some wrapper template<class T> class MapAllocatorWrapper : std::allocator<std::pair<T,std::string>>, I can send Foo<MapAllocatorWrapper, MapToType<std::string>::FromKey after slight adjustments.

not able to fully undertand how template template parameters work

I was reading Modern C++ design and not able to fully understand how template template parameter works.
For example as given in this article http://www.informit.com/articles/article.aspx?p=376878 we can create a Stack with template parameters of type and containers .
If we use just type and container as parameter to template Stack then it may create some issues like
template <typename T, class Cont>
class Stack;
template <typename> class List;
Stack<int, List<int> > aStack1; // OK
Stack<double, List<int> > aStack2; // legal, not OK
Stack<std::string, Deque<char *> > aStack3; // error!
In above code I can understand that aStack1 is fine,aStack2 and aStack3 are issues because if incompatible types between Stack element type and container element type.
As per article this can be resolved if we use template template parameter
template <typename T, template <typename> class Cont>
class Stack;
Stack<int,List> aStack1;
Stack<std::string,Deque> aStack2;
My doubt here is how can Deque knows that its element type is std::string or List element type is int???Is this done by template argument deduction?
Here we are creating a Deque with type T .
If we have defined stack as
template <typename T,template U, template <typename> class Cont>
class Stack;
then how can we instantiate Stack
Stack<std::string,int,Deque> // will this work????
My doubt here is how can Deque knows that its element type is std::string or List element type is int???Is this done by template argument deduction?
No, you explicitly instantiate the class template in the implementation of Stack which is likely to be implemented as:
template <typename T, template <typename> class Cont>
class Stack
{
Cont<T> _container; //here you use `T` as template argument to `Cont`
Cont<int> _ints; //you can do even this if you need a containter of int.
//though in this case you don't need this.
public:
void push(T const & data)
{
_container.push_back(data);
}
//etc
};
For a template to be instantinated its definition is required. In your examples, you only provide the template declaration, which is not enough for it to be instantinated. The definition gives the implementation which details how the template parameters are used. For example,
template <typename T, template <typename> class Cont>
class Stack : Cont<T>
{
typedef Cont<T> my_container;
public:
using my_container::push_back;
/* more details here */
};
Stack<int,List> aStack1;
Stack<std::string,Deque> aStack2;
implements aStack1 as derived from a List<int> and astack2 as derived from a Deque<std::string>.

Can you use C++ templates to specify a collection type and the specialization of that type?

Example, I want to specialize a class to have a member variable that is an stl container, say a vector or a list, so I need something like:
template <class CollectionType, class ItemType>
class Test
{
public:
CollectionType<ItemType> m_collection;
};
So I can do:
Test t = Test<vector, int>();
t.m_collection<vector<int>> = vector<int>();
But this generates
test.cpp:12: error: `CollectionType' is not a template
What you want is a template template parameter:
template <template <typename> class CollectionType, class ItemType>
class Test
{
public:
CollectionType<ItemType> m_collection;
};
What we did here is specifying that the first template parameter, i.e. CollectionType, is a type template. Therefore, Test can only be instantiated with a type that is itself a template.
However, as #Binary Worrier pointed in the comments, this won't work with STL containers since they have 2 template parameters: one for the elements type, the other one for the type of the allocator used for managing storage allocation (which has a default value).
Consequently, you need to change the first template parameter so that it has two parameters:
template <template <typename,typename> class CollectionType, class ItemType>
class Test
{
public:
CollectionType<ItemType> m_collection;
};
But wait, that won't work either! Indeed, CollectionType awaits another parameter, the allocator... So now you have two solutions:
1 . Enforce the use of a particular allocator:
CollectionType<ItemType, std::allocator<ItemType> > m_collection
2 . Add a template parameter for the allocator to your class:
template <template <typename,typename> class CollectionType,
class ItemType,
class Allocator = std::allocator<ItemType> >
class Test
{
public:
CollectionType<ItemType, Allocator> m_collection;
};
So as you see, you end up with something rather complicated, which seems really twisted to deal with STL containers...
My advice: see Greg Rogers' answer for a better approach :)!
Why not do it like this?
template <class CollectionType>
class Test
{
public:
CollectionType m_collection;
};
Test t = Test<vector<int> >();
t.m_collection = vector<int>();
If you need the itemtype you can use CollectionType::value_type.
EDIT: in response to your question about creating a member function returning the value_type, you do it like this:
typename CollectionType::value_type foo();
You add the typename because CollectionType has not been bound to an actual type yet. So there isn't a value_type it could look up.
Comeau online likes this:
#include <vector>
template <template <class T, class A = std::allocator<T> > class CollectionType, class ItemType>
class Test
{
public:
CollectionType<ItemType> m_collection;
};
void foo()
{
using std::vector;
Test<vector,int> t = Test<vector, int>();
t.m_collection = vector<int>();
}