C++ template inheritance 2 arguments - c++

is it possible to inherit everything from template class and just rewrite some of it's functions specialized to int, double or float?
Is there a way to write something similar to this?
template<typename T, size_t N>
class Container<int, N> : public Container<T, N> {
};

No, you need to use a different name.
template<class T, size_t N>
struct ContainerBase:std::array<T,N> {
// some methods here
};
template<class T, size_t N>
struct Container:ContainerBase<T,N> {
// inherit any constructors:
using ContainerBase<T,N>::ContainerBase;
};
template<size_t N>
struct Container<int, N>:ContainerBase<int,N> {
using ContainerBase<int,N>::ContainerBase;
// overload (not override) methods here
};

Related

Simultaneous recursion over multiple parameter packs

Is it possible in C++ to simultaneously recurse over multiple (in my case two) parameter packs? How?
To give a better idea of exactly what I'm asking, I currently have a class template as follows:
template<template<typename> class C, typename... Rest>
class Inherit {};
template<template<typename> class C, typename T, typename... Rest>
class Inherit<C, T, Rest...> :
public C<T>,
public Inherit<C, Rest...>
{
};
I'm using this class template in another class which takes a parameter pack Ts to have that class inherit multiple times from a templated interface. For example:
template<typename T>
class Interface
{
...
};
template<typename... Ts>
class Example :
public Inherit<Interface, Ts...>
{
};
Now, what I would like to do is this:
template<typename T, const char* N>
class Interface
{
...
};
template<typename... Ts, const char*... Ns>
class Example :
public Inherit<Interface, Ts..., Ns...>
{
};
I need a templated Inherit class that can recurse and pick an element off of both the Ts and the Ns parameter pack at once (assuming that parameter pack Ts and Ns have the same length). I'm not versed enough in template programming to know if this is even possible?
I would argue that you don't need this Inherit class template based on the examples you gave. Here's how I'd write your first example:
template<typename T>
class Interface
{
...
};
template<typename... Ts>
class Example : public Interface<Ts>...
{
};
and your second example:
template<typename T, const char* N>
class Interface
{
};
template<typename, const char*...>
class Example;
template<typename... Ts, const char*... Ns>
class Example<std::tuple<Ts...>, Ns...> : public Interface<Ts, Ns>...
{
};
template <typename... Ts, const char*... Ns> class Example; is wrong.
You might want something like (no recursion)
template<typename T, const char* N>
class Interface
{
// ...
};
template<typename T, typename Names> class Example;
template<typename... Ts, const char*... Ns>
class Example<std::tuple<Ts...>, std::integer_sequence<const char*, Ns...>> :
public: Interface<Ts, Ns>...
{
};
Interface<Ts, Ns>... requires that both packs have the same size.

Simplify template arguments

I need to create a template class depending on a simple struct like this. It has a method AddNew to insert new element for its tuple member.
struct Type
{
int i;
char c;
};
template<typename ...T>
class A
{
std::tuple<T...> ts;
public:
A(T&&...t):ts(std::move(t)...){}
void AddNew(T t)
{
ts=std::tuple_cat(ts,t);
}
};
int main()
{
A<Type,Type,Type,Type> a({1,'a'},{2,'t'},{3,'c'},{4,'g'});
Type t{5,'x'};
a.AddNew(t);
}
Problem I don't want to write down Type... 4 times like in main. I like something like A<Type,4> when I initialize A with 4 Types or A<Type, 100> for 100 Types.
You probably just want vector:
struct Type
{
int i;
char c;
};
template<typename T>
class A
{
std::vector<T> data;
public:
template <typename ... Ts>
A(Ts&&...ts) : data(std::forward<Ts>(ts)...){}
void AddNew(const T& t)
{
data.push_back(t);
}
};
int main()
{
A<Type> a(Type{1,'a'}, Type{2,'t'}, Type{3,'c'}, Type{4,'g'});
Type t{5,'x'};
a.AddNew(t);
}
you may partially specialize A for some helper 'tag' type:
template<typename T, std::size_t N>
struct repeat{};
template<typename... T>
struct repeat_unpack{ using type = A<T...>; };
template<typename T, int N, typename... V >
struct repeat_unpack<repeat<T,N>,V...>: std::conditional_t<(N>1),
repeat_unpack<repeat<T,N-1>,T,V...>,
repeat_unpack<T,V...>
>{};
template<typename T, int N>
class A<repeat<T,N>>: repeat_unpack<repeat<T,N>>::type {};
// to be used as
A<repeat<Type,3>>
the above uses inheritance to avoid code duplication, so you'll probably need to lift some members (eg. constructors) to the partial specialization as well.
Otherwise, just use an alias
template<class T, std::size_t N>
using A_nth = typename repeat_unpack<repeat<T,N>>::type;
A_nth<Type,3>
Just one more thing, given that you're using tuple internally, it should be easy to simply specialize for std::array and use it instead, being tuple compatible ...
PS. obviously, your AddNew() code makes no sense as is, I'd assume it's just a code snippet typo ...

std::tuple of std::shared_ptr of template parameter pack

I want to implement a class template that:
behaves like a function
it's input and output variables are all shared.
relatively easy to use.
As a result, I construct the following:
// all input/output variable's base class
class basic_logic_parameter;
// input/output variable, has theire value and iterators to functions that reference to this variable
template <typename FuncIterator, typename ValueType>
class logic_parameter
:public basic_logic_parameter
{
private:
std::list<FuncIterator> _refedFuncs;
ValueType _val;
public:
};
// all `function`'s base class
class basic_logic_function
{
public:
virtual ~basic_logic_function() = 0;
};
// the function, has input/output variable
template <typename FuncIterator, typename R, typename... Args>
class logic_function_base
:public basic_logic_function
{
private:
std::shared_ptr<logic_parameter<FuncIterator, R>> _ret;
std::tuple<std::shared_ptr<logic_parameter<FuncIterator, Args>>...> _args;
public:
template <std::size_t N>
decltype(auto) arg()
{
return std::get<N>(_args);
}
template <std::size_t N>
struct arg_type
{
typedef std::tuple_element_t<N> type;
};
template <std::size_t N>
using arg_type_t = arg_type<N>::type;
decltype(auto) ret()
{
return _ret;
}
};
I wish to use as these like:
// drawing need color and a pen
struct Color
{
};
struct Pen
{
};
struct Iter
{
};
class Drawer
:public logic_function_base<Iter, void(Color, Pen)>
{
public:
void draw()
{
arg_type_t<0> pColor; // wrong
}
}
My compiler can not pass this code through, why? I just want convert a template parameter pack to std::tuple of std::shared_ptr of them.
for example:
Given struct A, int, struct C, I want to have:
std::tuple<
std::shared_ptr<logic_parameter<A>>,
std::shared_ptr<logic_parameter<int>>,
std::shared_ptr<logic_parameter<C>>,
>
The problem (once the small errors are fixed1) is that you instantiate:
logic_function_base<Iter, void(Color, Pen)>
...meaning that FuncIterator is Iter and R is void(Color, Pen), so Args is emtpy <>, so decltype(_args) is an empty std::tuple<>, and your code fails to obtain the type of the 0th element of an empty tuple, which is legit.
What you want is partial specialization of logic_function_base:
template <typename F, typename T>
class logic_function_base;
template <typename FuncIterator, typename R, typename... Args>
class logic_function_base<FuncIterator, R(Args...)>: public basic_logic_function {
};
1 Small mistakes in your current code:
template <std::size_t N>
struct arg_type
{
typedef std::tuple_element_t<N, decltype(_args)> type; // Missing the tuple type
};
template <std::size_t N>
using arg_type_t = typename arg_type<N>::type; // Missing a typename
This may not answer your whole question, but you could use the following trait to wrap tuple element types.
template <typename T> struct wrap;
template <typename... T>
struct wrap<std::tuple<T...>> {
using type = std::tuple<std::shared_ptr<logic_parameter<T>>...>;
}
template <typename T>
using wrap_t = typename wrap<T>::type;
You can then use it like this:
std::tuple<int,double,char> t1;
wrap_t<decltype(t)> t2;
The type of t2 is std::tuple<std::shared_ptr<logic_parameter<int>>,std::shared_ptr<logic_parameter<double>>,std::shared_ptr<logic_parameter<char>>>.

How can I use `std::array` for a template parameter of the form `template<typename> class`?

Please consider the following tree class
template<typename T, template<typename> class Tuple>
class tree
{
private:
T m_value;
Tuple<tree> m_children;
};
template<typename T, std::size_t N>
using static_tree = tree<T, std::array<T, N>>;
which is not well-defined. std::array<T, N> is not a suitable template parameter for Tuple. I assume the intend of static_tree is clear. We could do something like
template<std::size_t N>
struct helper
{
template<typename T>
using type = std::array<T, N>;
};
template<typename T, std::size_t N>
using static_tree = tree<T, helper<N>::template type>;
Is there any other option without the helper class?
Rather than having the helper function be an exception for std::array, I'd propose that be the rule. Instead of taking a template template parameter, take a metafunction class parameter. Template metaprogramming is a lot easier when everything everywhere is a type (avoiding template templates and non-type arguments):
template<typename T, typename TupleMfn>
class tree
{
private:
using Tuple = TupleMfn::template apply<tree>;
T m_value;
Tuple m_children;
};
with:
template <size_t N>
struct array_builder {
template <class T>
using apply = std::array<T, N>;
};
template <typename T, size_t N>
using static_tree = tree<T, array_builder<N>>;
This will make it easier to use with other kinds of containers as well, since we can make a wrapper for template templates that gives us back a metafunction:
template <template <typename...> class X>
struct as_metafunction {
template <class... Args>
using apply = X<Args...>;
};
template <typename T>
using vector_tree = tree<T, as_metafunction<std::vector>>;
If you're feeling especially feisty, you can provide a metaprogramming-friendly version of std::array:
template <class T, class N>
struct meta_array : std::array<T, N::value> // obviously I am terrible at naming things
{ };
template <size_t N>
using size_t_ = std::integral_constant<size_t, N>;
And then provide placeholder args for tree to apply:
template <class T, size_t N>
using static_tree = tree<T, meta_array<_, size_t_<N>>>;
template <class T>
using vector_tree = tree<T, std::vector<_>>;
I think your question contains a fundamental problem which is different from what you were explicitly asking (it threw me off a bit in the previous iteration of this answer). Combining the different parts of your question, you're essentially trying to instantiate some tree class that has a member that is an std::array of the same class as well. This is obviously impossible. You probably want that the tree should hold some Tuple of pointers (smart or otherwise).
One way to do so would be to use your helper class, but modifying the class to
template<typename T, template<typename> class Tuple>
class tree
{
// Indirection (I'm omitting the question of whether these should be
// smart pointers.
Tuple<tree<T, Tuple> *> m_children;
};
A different way would make Tuple a regular template parameter, as follows:
#include <array>
#include <type_traits>
template<typename T, class Tuple>
class tree
{
private:
static_assert(
std::is_same<void *, typename Tuple::value_type>::value,
"Tuple must be a container of void *");
private:
T m_value;
Tuple m_children;
};
template<typename T, std::size_t N>
using static_tree = tree<T, std::array<void *, N>>;
int main()
{
static_tree<int, 8> t;
}
On the one hand, the helper class has been eliminated. OTOH, Tuple is a container of void *: the instantiators are aware of this, and the class internally needs to perform casts. It's a tradeoff. I would stick to your original version (with the modifications suggested, of course).
A class X cannot contain multiple copies of actual instances of class X, except logically.
An if we have
struct X {
std::array<X, 2> data;
};
the only possible size for X is infinity, as sizeof(X) = 2*sizeof(X), and all types in C++ have sizeof(X)>=1.
C++ does not support infinitely large types.
Your second problem is that type instances are not templates.
template<typename T, template<typename> class Tuple>
class tree
this takes a type T and a template Tuple. The second argument is not a type.
template<typename T, std::size_t N>
using static_tree = tree<T, std::array<T, N>>;
here, your second argument is a type, not a template.
template<std::size_t N>
struct array_of_size {
template<class T>
using result=std::array<T,N>;
};
template<typename T, std::size_t N>
using static_tree = tree<T, array_of_size<N>::template result>;
would, other than the above "infinite size problem", solve your problem. Here we are passing the template array_of_size<N>::result to tree.
To solve the infinite size problem, you must store pointers (or something equivalent) in the array. So we get:
template<std::size_t N>
struct array_of_ups_of_size {
template<class T>
using result=std::array<std::unique_ptr<T>,N>;
};
template<typename T, std::size_t N>
using static_tree = tree<T, array_of_ups_of_size<N>::template result>;
and now your static_tree has N children, each of which is a unique_ptr to a similar static_tree.
This still doesn't work, because of destructor issues.
template<typename T, template<typename> class Tuple>
class tree
{
private:
T m_value;
Tuple<tree> m_children;
public:
~tree();
};
template<typename T, template<typename> class Tuple>
tree<T,Tuple>::~tree() = default;
I think the above fixes it, strange as it may seem.
Basically, when you make the array of children, the tree type is incomplete. At destruction, delete is called. At that point, tree must be complete. By deferring the dtor, we hopefully deal with the problem.
I am uncertain if this technique is required for templates, but it is for non-template classes.
Or you could implement a template parameter bind as you suggested (slightly more general than your helper):
template<std::size_t N, template<typename,std::size_t> class T>
struct bind2nd
{
template<typename F>
using type = T<F,N>;
};
template<std::size_t N, typename T>
using static_tree = tree<T, bind2nd<N,std::array>::template type>;

Declare "container" object from templated template class and variadic templates

I need to declare a class which could store different kind of containers. i.e. It would be nice if it could handle std::bitset and std::array.
However, these two classes need a different of template arguments...
Is it possible (and possibly, how) to use templated template classes and variadic templates to declare this kind of class?
Example (but wrong):
template<template <typename..., std::size_t> class Container,
std::size_t N,
typename... Args>
class Base_Class
{
...
Container<Args..., N/2> container;
};
The compiler complains that N/2 is not a type. Obviously, for both std::array and std::bitset I need the size to be the last template parameter... Is it possible to code this crazyness?
Thank you!
EDIT:
As far as I am concerned, the main problem is that variadic templates can only be expanded on the right, therefore the variadic parameter must be the last one. Anyone know if there are any plans to allow the following syntax in C++17?
template<typename... Args, typename T>
struct A
{};
Anton's answer can be made somewhat less container-specific by using template template parameters for the speciliasations of ResizedContainer:
namespace detail {
template<typename Container>
struct ResizedContainer;
template<template<typename,std::size_t> class Container,
typename T, std::size_t N>
struct ResizedContainer<Container<T,N>> {
using type = Container<T,N/2>;
};
template<template<std::size_t> class Container,
std::size_t N>
struct ResizedContainer<Container<N>> {
using type = Container<N/2>;
};
}
#include <array>
#include <bitset>
template<typename Container>
class Base_Class {
typename detail::ResizedContainer<Container>::type container;
};
int main() {
Base_Class<std::array<int,4>> a;
Base_Class<std::bitset<5>> b;
}
Maybe something like this:
namespace detail {
template<typename Container>
struct ResizedContainer;
template<typename T, size_t N>
struct ResizedContainer<std::array<T, N>> {
using type = std::array<T, N/2>;
};
template<size_t N>
struct ResizedContainer<std::bitset<N>> {
using type = std::bitset<N/2>;
};
}
template<typename Container>
class Base_Class {
typename detail::ResizedContainer<Container>::type container;
};
int main() {
Base_Class<std::array<int, 4>> a;
Base_Class<std::bitset<5>> b;
}