Home > Back-end >  Is it possible to declare template parameters within the template arguments of another template para
Is it possible to declare template parameters within the template arguments of another template para

Time:11-09

I am attempting to create a class template that takes an std::array as a template argument. Currently, the declaration is as follows:

template<typename T, std::size_t N, std::array<std::size_t, N> A>
class Foo {
    ...
}

This is quite unfortunate, however, as I will typically be initializing A with an initializer list, which makes the need to specify N tedious and redundant:

// Current
Foo<int, 3, {5, 3, 4}> bar;

// Preferred
Foo<int, {5, 3, 4}> baz;

I have tried to use something similar to template template parameters to no avail:

template<typename T, template<std::size_t N> std::array<std::size_t, N> A>

Is there a way to accomplish what I'm trying to do, or otherwise 'hide' N by placing it after A in the parameter list?

CodePudding user response:

There is the option of adding a wrapper, so the following code would work:

Foo2<as_std_array<5, 3, 4>> baz2;

With as_std_array being defined as:

template<auto item, auto... items>
struct as_std_array {
    using TYPE = std::common_type_t<decltype(item), decltype(items)...>;
    constexpr static std::size_t SIZE = sizeof...(items) 1;
    constexpr static auto ARRAY = std::array<decltype(item), SIZE>{item, items...};
};

And Foo, Foo2 as:

template<std::size_t N, typename T, std::array<T, N> A>
struct Foo {
    constexpr static auto ARRAY = A;
};

template<typename T>
class Foo2: public Foo<T::SIZE, typename T::TYPE, T::ARRAY>{};

Then the following works:

Foo2<as_std_array<5, 3, 4>> baz2;    
static_assert(baz2.ARRAY == std::array{5, 3, 4});

Link to code

CodePudding user response:

You might be able to use c 20 auto template arguments:

template<typename T, auto A>

Live demo using Boost to demangle the type:

Live On Compiler Explorer

#include <array>
#include <boost/core/demangle.hpp>
#include <iostream>

template<typename T, auto A>
struct Foo {
    void bar() const {
        std::cout << boost::core::demangle(typeid(decltype(A)).name()) << "\n";
    }
};


int main() {
    Foo<int, std::array{3,4,7}> f;
    Foo<int, std::array{'a', 'b'}> g;

    f.bar();
    g.bar();
}

Prints

std::array<int, 3ul>
std::array<char, 2ul>

Variadic Packs

Otherwise I suggest just passing the array elements as a pack:

template<typename T, auto... A>

See it Live Online (C 17)

#include <type_traits>
#include <iostream>

template<typename T, auto...>
struct Foo {
    void bar() const {
        std::cout << __PRETTY_FUNCTION__ << "\n";
    }
};


int main() {
    Foo<int, 3,4,7> f;
    Foo<int, 'a', 'b'> g;

    f.bar();
    g.bar();
}

Prints

void Foo<T, <anonymous> >::bar() const [with T = int; auto ...<anonymous> = {3, 4, 7}]
void Foo<T, <anonymous> >::bar() const [with T = int; auto ...<anonymous> = {'a', 'b'}]
  • Related