Template class with template container
You should use template template parameters:
template<typename T, template <typename, typename> class Container>
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
class MyMultibyteString
{
Container<T, std::allocator<T>> buffer;
// ...
};
This would allow you to write:
MyMultibyteString<int, std::vector> mbs;
Here is a compiling live example. An alternative way of writing the above could be:
template<typename T,
template <typename, typename = std::allocator<T>> class Container>
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
class MyMultibyteString
{
Container<T> buffer; // <== No more need to specify the second argument here
// ...
};
And here is the corresponding live example.
The only thing you have to pay attention to is that the number and type of arguments in the template template parameter declaration must match exactly the number and type of arguments in the definition of the corresponding class template you want to pass as a template argument, regardless of the fact that some of those parameters may have default values.
For instance, the class template std::vector
accepts two template parameters (the element type and the allocator type), although the second one has the default value std::allocator<T>
. Because of this, you could not write:
template<typename T, template <typename> class Container>
// ^^^^^^^^
// Notice: just one template parameter declared!
class MyMultibyteString
{
Container<T> buffer;
// ...
};
// ...
MyMultibyteString<int, std::vector> mbs; // ERROR!
// ^^^^^^^^^^^
// The std::vector class template accepts *two*
// template parameters (even though the second
// one has a default argument)
This means that you won't be able to write one single class template that can accept both std::set
and std::vector
as a template template parameter, because unlike std::vector
, the std::set
class template accepts three template parameters.
c++ template class; function with arbitrary container type, how to define it?
Traits solution.
Generalize not more than needed, and not less.
In some cases that solution might not be enough as it will match any template with such signature (e.g. shared_ptr
), in which case you could make use of type_traits
, very much like duck-typing (templates are duck typed in general).
#include <type_traits>
// Helper to determine whether there's a const_iterator for T.
template<typename T>
struct has_const_iterator
{
private:
template<typename C> static char test(typename C::const_iterator*);
template<typename C> static int test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
// bar() is defined for Containers that define const_iterator as well
// as value_type.
template <typename Container>
typename std::enable_if<has_const_iterator<Container>::value,
void>::type
bar(const Container &c, typename Container::value_type const & t)
{
// Note: no extra check needed for value_type, the check comes for
// free in the function signature already.
}
template <typename T>
class DoesNotHaveConstIterator {};
#include <vector>
int main () {
std::vector<float> c;
bar (c, 1.2f);
DoesNotHaveConstIterator<float> b;
bar (b, 1.2f); // correctly fails to compile
}
A good template usually does not artificially restrict the kind of types for which they are valid (why should they?). But imagine in the example above you need to have access to an objects const_iterator
, then you can use SFINAE and type_traits to put those constraints on your function.
Or just to as the standard library does
Generalize not more than needed, and not less.
template <typename Iter>
void bar (Iter it, Iter end) {
for (; it!=end; ++it) { /*...*/ }
}
#include <vector>
int main () {
std::vector<float> c;
bar (c.begin(), c.end());
}
For more such examples, look into <algorithm>
.
This approach's strength is its simplicity and is based on concepts like ForwardIterator. It will even work for arrays. If you want to report errors right in the signature, you can combine it with traits.
std
containers with signature like std::vector
(not recommended)
The simplest solution is approximated by Kerrek SB already, though it is invalid C++. The corrected variant goes like so:
#include <memory> // for std::allocator
template <template <typename, typename> class Container,
typename Value,
typename Allocator=std::allocator<Value> >
void bar(const Container<Value, Allocator> & c, const Value & t)
{
//
}
However: this will only work for containers that have exactly two template type arguments, so will fail miserably for std::map
(thanks Luc Danton).
Any kind of secondary template arguments (not recommended)
The corrected version for any secondary parameter count is as follows:
#include <memory> // for std::allocator<>
template <template <typename, typename...> class Container,
typename Value,
typename... AddParams >
void bar(const Container<Value, AddParams...> & c, const Value & t)
{
//
}
template <typename T>
class OneParameterVector {};
#include <vector>
int main () {
OneParameterVector<float> b;
bar (b, 1.2f);
std::vector<float> c;
bar (c, 1.2f);
}
However: this will still fail for non-template containers (thanks Luc Danton).
Class template with variable type of container
Sure you can. This is called a container adapter. std::queue
itself is a container adapter and looks like
template<class T, class Container = std::deque<T>>
class queue
{
//...
};
Doing that though requires you to use something like
std::queue<int, std::vector<int>> foo;
If you want to change the container. If you do not want to have to specify the template type of the container then you can use a template template like
template<class T, template<typename...> class Container = std::queue>
class Myclass
{
Container<T> cont;
};
and you can use it like
Myclass<int, std::set> foo;
To change it to use a std::set
instead of the default std::queue
.
Container of template classes without template parameter
A possible implementation would be using the double dispatching:
#include <iostream>
#include <list>
struct visitor;
struct dispatchable {
virtual void accept(visitor &v) = 0;
};
template <class>
struct base;
struct visitor {
template<typename T>
void visit(base<T> &);
};
template <class T>
struct base: dispatchable {
T val;
base(T newVal): val(newVal) {};
void accept(visitor &v) override { v.visit(*this); }
};
struct derivedInt : base<int> {
derivedInt(int newVal): base(newVal) {};
};
struct derivedDouble : base<double> {
derivedDouble(double newVal): base(newVal) {};
};
template<>
void visitor::visit(base<int> &) {
std::cout << "int" << std::endl;
}
template<>
void visitor::visit(base<double> &) {
std::cout << "double" << std::endl;
}
int main ( void ) {
visitor v{};
std::list <dispatchable*> coll;
coll.push_back(new derivedInt{42});
coll.push_back(new derivedDouble{.42});
for(auto d: coll) d->accept(v);
}
This way, you have only to define the specialized function that deals with the new base<T>
type you want to introduce.
As an example, if you want to use base<char>
, you have to define:
template<>
void visitor::visit(base<char> &) {
std::cout << "char" << std::endl;
}
Note that I supposed you want to treat each specialization of base<T>
in a different way. Otherwise, it's enough to define the generic member function visitor::visit
and drop the specializations.
Side note: do not use naked pointers.
This is an example. In production code, I'd use smart pointers instead.
Using a template class in STL containers
Templates are a compile-time code generation construct. If you need an heterogeneous container of objects at compile-time, then you can use std::tuple
:
std::tuple my_tuple{A<int>{}, A<char>{}, A<double>{}};
If you need an heterogeneous container of objects at run-time, you do need some sort of polymorphism. Using a base class with virtual
methods is a valid option. If you know all the possible choice of types your object can be in advance, you can also use std::variant
:
using my_a = std::variant<A<int>, A<char>, A<double>>;
std::vector<my_a> vec;
In this case, my_a
can either be A<int>
, A<char>
, or A<double>
at any given time. The active alternative can change at run-time.
Pass a container type as the typename of a template in c++
With minimum changes to make it work, your code could be this:
#include <array>
template <typename T>
int find_size(const T& t)
{
return (t.size());
}
int main(void)
{
std::array<int, 10> test;
for (int i = 0; i < 10; i++)
{
test[i] = i;
}
find_size(test);
}
basically I want my function to be able to take an abritary type of container
Thats exactly what the above does. It works for any container type T
that has a size()
.
If you actually want to parametrize find_size
on a template rather than a type, then you can use a template template parameter:
#include <array>
template <template<class,std::size_t> class C>
int find_size(const C<int,10>& t)
{
return (t.size());
}
int main(void)
{
std::array<int, 10> test;
for (int i = 0; i < 10; i++)
{
test[i] = i;
}
find_size<std::array>(test);
}
However, using this is either more complicated than illustrated here, or of more limited use than the above: For the function parameter you need a type not just a template, and this find_size
will only work with a template C
that has 2 parameters, one type and one non-type parameter of type size_t
(and I am actually not aware of any other container but std::array
with that template parameters).
TL;DR: This is not a use case where a template template parameter is needed.
Is it possible to pass the template of a container to the template of a function?
Yes, but you don't need it here.
Related Topics
How Can Duff's Device Code Be Compiled
When Does a Std::Vector Reallocate Its Memory Array
Boost C++ Regex - How to Get Multiple Matches
Displacement Map Filter in Opencv
Difference Between Opening a File in Binary VS Text
How to #Include When There Is a Circular Dependency
Protected Data in Parent Class Not Available in Child Class
C++: Unresolved External Symbol _Sprintf and _Sscanf in Visual Studio 2015
Why Is a C++ Reference Considered Safer Than a Pointer
How Would One Call Std::Forward on All Arguments in a Variadic Function
Restrict Passed Parameter to a String Literal
Eigen::Ref for Concatenating Matrices
How to Design Proper Release of a Boost::Asio Socket or Wrapper Thereof
Converting Integer into Array of Digits
Error Redeclaring a for Loop Variable Within the Loop