Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 649
0xbadf00d Avatar asked Mar 19 '16 22:03

0xbadf00d


People also ask

Can we pass Nontype parameters to templates?

Template classes and functions can make use of another kind of template parameter known as a non-type parameter. A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument.

What is typename in C++ template?

" typename " is a keyword in the C++ programming language used when writing templates. It is used for specifying that a dependent name in a template definition or declaration is a type.

What can the template parameter in C++ template definition be?

A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.

Can a template be a template parameter?

A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)

What is the difference between typename and class in template?

There is no difference between using <typename T> OR <class T> ; i.e. it is a convention used by C++ programmers.


2 Answers

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<_>>;
like image 75
Barry Avatar answered Nov 14 '22 23:11

Barry


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).

like image 44
Ami Tavory Avatar answered Nov 14 '22 22:11

Ami Tavory