Template class with template container - c++

How can I declare template class (adaptor) with different containers as template arguments?
For example, I need to declare class:
template<typename T, typename Container>
class MyMultibyteString
{
Container buffer;
...
};
And I want it to my based on vector. How to make it hard-defined? (to prevent someone from writing such declaration MyMultibyteString<int, vector<char>>).
Moreover, how to implement such construction:
MyMultibyteString<int, std::vector> mbs;
without passing template argument to 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.

Another approach to solve this is by using variadic templates and with that you can use any container as suggested in comments above and here is the implemenation :
template<template <typename... Args> class Container,typename... Types>
class Test
{
public:
Container<Types...> test;
};
int main()
{
Test<std::vector,int> t;
Test<std::set,std::string> p;
return 0;
}

If you look at the definitions of list and vector from cplusplus.com, for example they are:
template < class T, class Alloc = allocator<T> > class list;
and
template < class T, class Alloc = allocator<T> > class vector;
So this should go as the type of the container, the other template parameter is the type of the elements. As an example this program will output 3:
#include <iostream>
#include <list>
using namespace std;
template <template <typename...> typename C, typename T>
struct Cont {
C<T> info;
};
int main(void)
{
Cont<list, int> cont;
cont.info.push_back(3);
cout << cont.info.front() << endl;
return 0;
}

Related

Explicitly specify template template types

I'm learning templates. If I mix up the concepts template / template-type / template-argument, please correct me.
I'm trying to write a template function that creates an object and returns it. The type of the object comes from the template argument that has to be explicitly specified.
result = createObject<ObjectType>();
This object though is supposed to be a template. A container for example. And the function is supposed to know the type of the object and its template arguments. Ex:
result = createObject<Container<ElementType>>();
I've tried to solve it with template template parameter:
template <template<class> class ContainerType, class ElementType>
auto createObject()
{
ContainerType<ElementType> result;
//do stuff...
return result;
}
//...
template<typename T>
struct Vector{};
//...
//const auto random_vec = createObject<Vector<float>>(); // ERROR.
const auto random_vec = createObject<Vector, float>();
The second case works, the first doesn't. It says candidate template ignored: invalid explicitly-specified argument for template parameter 'ContainerType'.
Is it possible to make it work like the first case? Give it something like Vector<float> and it can deduce the ContainerType to Vector and ElementType to float? Is it possible to overload or specialize this function so that it handles certain types of containers differently? Should I use concepts?
The usual way to do decomposition like this is via partial specialization, which requires a helper class template:
namespace detail {
template<class> struct create; // undefined
template<template<class T> class C,class T>
struct create<C<T>> {
static C<T> make() {/* … */}
};
}
template<class T>
T createObject() {return detail::create<T>::make();}
The primary template can be defined if you want to support the general case, and other specializations may be added for other kinds of templates like std::array.
You could create a type trait to check if the type is instantiated from a template:
#include <type_traits>
// trait to check if the type is instantiated from a template
template<typename T>
struct is_template_instance_type : std::false_type {};
template<template<class,class...> class C, class T, class... Rest>
struct is_template_instance_type<C<T,Rest...>> : std::true_type {
using class_type = C<T,Rest...>;
using value_type = T;
// using rest_types = std::tuple<Rest...>;
};
// Helper variable template - if needed for something later
template<class T>
inline constexpr bool is_template_instance_type_v = is_template_instance_type<T>::value;
You could then add overloads:
template<class T, class C = is_template_instance_type<T>, class U = typename C::class_type>
auto createObject() {
U result;
// typename C::value_type x; // if you need the value type
return result;
}
template<template<class,class...> class C, class T, class... Rest>
auto createObject() {
return createObject< C<T,Rest...> >();
}
And it would then work with Vector<float>, Vector, float but not float for example.
Demo
You can simply go like this:
template<typename T, typename V = typename T::value_type>
T createObject()
{
T t {}; // T will be e.g std::vector<int>
V v {}; // V will be int
// do work...
t.push_back(v++);
t.push_back(v++);
// ...work done
return t;
}
Than you can use it like this:
int main ()
{
auto obj1 = createObject<std::vector<int>>();
auto obj2 = createObject<std::list<double>>();
return 0;
}

what is this template doing?

from https://github.com/wjakob/tbb/blob/master/include/tbb/tbb_allocator.h#L150
template <typename T, template<typename X> class Allocator = tbb_allocator>
class zero_allocator : public Allocator<T>
{...}
what I understand is that this is a definition for a new class, that inherits from the Allocator type visible in that translation unit .
the part that I don't get is template<typename X> class Allocator = tbb_allocator .
according to the tbb docs the zero_allocator takes 2 inputs, the type T and how many objects of type T you need to allocate . the zero_allocator also inheriths from the tbb_allocator which in turns defaults to a "standard" malloc/free behaviour if TBB is not present when linking .
I still don't think I get that syntax, especially the template<typename X> class Allocator part .
Can you explain this syntax and what is achieving ?
template <typename T, template<typename X> class Allocator = tbb_allocator>
class zero_allocator : public Allocator<T>
{...}
What we have:
template starts declaration of a template
it is followed by the template parameter list:
<typename T, template<typename X> class Allocator = tbb_allocator>
The first template parameter is "some type" T
the next one is not a type, it is a template itself.
template<typename X> class Allocator
So the template class zero_allocator needs to get instantiated with first
parameter is any type T and with second parameter a template which itself takes on template parameter X must be given.
In addition, the second template parameter for zero_allocator can be left, in this case for Allocator parameter the template tbb_allocator is used.
Here a full compileable example:
template <typename Y>
class ExampleTemplate {};
// and the one which is used as the default
template <typename Y>
class tbb_allocator {};
template <typename T, template<typename X> class Allocator = tbb_allocator>
class zero_allocator : public Allocator<T>
{
// Lets use the type T:
T some_var; // in our example, this will be "int some_var"
// and here we use the Template Template thing...
Allocator<float> some_allocator;
};
int main()
{
zero_allocator< int, ExampleTemplate > za;
}

Function with template and Priority Queue

I'm trying to write Dijkstra's algorithm with Fibonacci Heap and Priority Queue. So I have a class (struct) for Fibonacci Heap
template<class T>
struct Fib {
...
};
and a function
template <template <class> class T>
void dijkstra(...) {
T<std::pair<double, int> > heap;
...
}
The problem is:
dijkstra<Fib>(...); // OK
dijkstra<std::priority_queue>(...); // doesn't compile
Why doesn't this compile, and how can I write it properly?
When you write a parameter like:
template<class> class T
that expects a class template with a single template argument. Fib is such a class template, it just takes T. However, std::priority_queue doesn't take a single template parameter. It takes three, even though two are defaulted:
template<
class T,
class Container = std::vector<T>,
class Compare = std::less<typename Container::value_type>
> class priority_queue;
If you're using C++11, you could simply enhance dijkstra to take an arbitrary class template:
template<template<class...> class T>
// ^^^
void dijkstra(...) {
Or write a single-template-parameter alias for std::priority_queue:
template <class T>
using vector_less_pq = std::priority_queue<T>;

Variadic template argument within template template parameter in a partial specialization

I am trying to develop a generic code that can select different containers types (std::vector, std::map, other), and perform operations over that container wrapper, but I got stuck with the following code:
enum class EContainnerType
{
EContainnerType_Normal,
EContainnerType_OR,
EContainnerType_AND
};
// Base declaration
template<EContainnerType, template<class ... > class ContainerType, class ... ArgType >
struct ConditionContainnerType
{
};
// Partial Specialization
template< template<class ... > class ContainerType, class ... ArgType >
struct ConditionContainnerType<EContainnerType::EContainnerType_OR, ContainerType<ArgType ... >, ArgType ...>
{
};
int main()
{
return 0;
}
The variadic template template argument does not compile, and I get this error:
main.cpp:33:108: error: wrong number of template arguments (2, should be 3)
struct ConditionContainnerType<EContainnerType::EContainnerType_OR,typename ContainerType<ArgType>, ArgType>
^
main.cpp:29:8: error: provided for 'template<EContainnerType <anonymous>, template<class> class ContainerType, class ArgType> struct ConditionContainnerType'
struct ConditionContainnerType
GOAL:
The main goal of this implementation is to perform a certain kind of operation of classification (OR, AND, XOR), this operation is performed on an element that is compared against the generic container.
The operation type is defined by the enum class, and a partial specialization is selected to do the action.
So, if I have a set {a,b,c,d,e} and I fill the set with a specific combination of elements say:
generic_container<Operation_type,generic_set_element> then I want the generic conditional container to perform an action selected by "Operation Type".
So if an element x is compared against the set, the generic container can perform the preselected action over the x element.
Your compiler is broken. The correct error messages look like this (g++ 4.8.2)
error: type/value mismatch at argument 2 in template parameter list for ‘template<EContainnerType <anonymous>, template<class ...> class ContainerType, class ... ArgType> struct ConditionContainnerType’
struct ConditionContainnerType<EContainnerType::EContainnerType_OR,ContainerType<ArgType ... >,ArgType ...>
^
error: expected a class template, got ‘ContainerType<ArgType ...>’
or this (clang 3.3)
error: template argument for template template parameter must be a class template or type alias template
struct ConditionContainnerType<EContainnerType::EContainnerType_OR,ContainerType<ArgType ... >,ArgType ...>
^
They are pretty self-explanatory.
So just remove the parameter.
//Partial Specialization
template< template<class ... > class ContainerType, class ... ArgType >
struct ConditionContainnerType<EContainnerType::EContainnerType_OR,
ContainerType, ArgType ...>
{
};
Now you can write
ConditionContainnerType<EContainnerType::EContainnerType_OR, std::vector, int> a;
ConditionContainnerType<EContainnerType::EContainnerType_OR, std::map, int, int> b;
without having to repeat the parameters twice.
The problem is that you cannot specialize a template-template parameter with a template-template parameter with given template arguments, like forcing:
ContainerType<Args...>
to match:
template <typename...> class BaseContainerType
because it is no longer a template template argument. Instead, a plain ContainerType name would need to be used here, without <Args...> part:
// Base declaration
template <template <typename...> class ContainerType>
struct ConditionContainnerType
{
};
// Partial specialization for two-parameter template template parameter
template <template <typename, typename> class ContainerType>
struct ConditionContainnerType<ContainerType>
{
};
You can, however, specialize the template type itself with a template-template parameter filled with arguments (even with an expanded parameter pack), which goes as follows:
// Base declaration
template <EContainnerType, typename ContainerType, typename... Args>
// ^^^^^^^^^^^^^^^^^^^^^^^ normal type here
struct ConditionContainnerType
{
};
// Partial specialization
template <template <typename...> class ContainerType, typename... Args>
// ^^^^^^^^ template here
struct ConditionContainnerType<EContainnerType::EContainnerType_OR,
ContainerType<Args...>,
// ^^^^^^^ expanded parameter pack
Args...>
{
};
or without a trailing parameter pack:
template <template <typename...> class ContainerType, typename... Args>
struct ConditionContainnerType<EContainnerType::EContainnerType_OR,
ContainerType<Args...>>
{
};
as well as specialize it for templated containers like std::vector<T, A> which take exactly two parameters: type T and allocator A:
template <template <typename, typename> class ContainerType, typename T, typename A>
struct ConditionContainnerType<EContainnerType::EContainnerType_OR,
ContainerType<T, A>,
T>
{
};
Tests:
int main()
{
// base, AND does not match OR
ConditionContainnerType<EContainnerType::EContainnerType_AND
, MyContainer<int>
, int>{};
// specialized, OR and parameter pack matches
ConditionContainnerType<EContainnerType::EContainnerType_OR
, MyContainer<int>
, int>{};
// base, OR matches, but parameters packs do not
ConditionContainnerType<EContainnerType::EContainnerType_OR
, MyContainer<float>
, int>{};
// vector, OR and parameter pack matches
ConditionContainnerType<EContainnerType::EContainnerType_OR
, std::vector<int>
, int>{};
// OR and no parameter-pack
ConditionContainnerType<EContainnerType::EContainnerType_OR
, std::vector<int>>{};
}
DEMO
If otherwise you aim to specialize your base declaration depending on concrete container type (std::vector, std::map), you can do this as follows:
// Base declaration
template <EContainnerType, template <typename...> class ContainerType>
struct ConditionContainnerType
{
};
// Partial specialization for std::vector
template <>
struct ConditionContainnerType<EContainnerType::EContainnerType_OR, std::vector>
// ^^^^^^^^^^^
{
};
// Partial specialization for std::map
template <>
struct ConditionContainnerType<EContainnerType::EContainnerType_AND, std::map>
// ^^^^^^^^
{
};
Tests:
int main()
{
// First specialization
ConditionContainnerType<EContainnerType::EContainnerType_OR, std::vector>{};
// Second specialization
ConditionContainnerType<EContainnerType::EContainnerType_AND, std::map>{};
}
DEMO 2

How to leave inner template(s) of template template (+template) parameters unspecified

How to make the following code compile?
#include "vector"
template<
template<class> class Container
> Container<int> f(int i) {
return Container<int>{i};
}
int main() {
return f<std::vector>(1)[0];
}
GCC-4.8.2 complains:
error: no matching function for call to 'f(int)'
note: template<template<class> class Container> Container<int> f(int)
The actual problem is how can I allow the caller to specify which Eigen linear algebra solver (e.g. http://eigen.tuxfamily.org/dox/classEigen_1_1BiCGSTAB.html) to use inside a function when the only change in code would be to comment out another line from:
Eigen::BiCGSTAB<Eigen::SparseMatrix<Scalar_T>> solver;
//Eigen::ConjugateGradient<Eigen::SparseMatrix<Scalar_T>> solver;
//Eigen::SimplicialCholesky<Eigen::SparseMatrix<Scalar_T>> solver;
Currently the function starts as:
template<
template<class> class Eigen_Solver_T,
class Scalar_T
> std::vector<Scalar_T> solve(...)
, I don't want the caller to also have to give Eigen::SparseMatrix or, instead, only give
Eigen::BiCGSTAB<Eigen::SparseMatrix<Scalar_T>>
as template parameter.
You need to understand what kind of template you have. In this case, std::vector has two type parameters:
template <typename T, template <typename, typename> class C>
C<T> foo()
{
return C<T>();
}
More generally you may like to use a variadic signature, which is permissible even for non-variadic templates:
template <typename T, template <typename...> class C>
C<T> bar()
{
return C<T>();
}
Usage:
foo<int, std::vector>(); // OK, returns std::vector<int, std::allocator<int>>
bar<int, std::vector>(); // OK, ditto
bar<int, std::set>(); // also OK
You cannot use std::vector as the template argument for your function since std::vector is defined as:
template<
class T,
class Allocator = std::allocator<T>
> class vector;
However, you can use the template aliasing feature of C++11 to accomplish your goal.
#include <vector>
template<
template<class> class Container
> Container<int> f(int i) {
return Container<int>{i};
}
template <typename T>
using MyVector = std::vector<T>;
int main() {
return f<MyVector>(1)[0];
}