3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

C++ template template parameterの使用例

Last updated at Posted at 2022-05-27

C++にはtemplate template parameterと呼ばれる魔術回路が備わっているが、活用している例が少ないのでメモ程度に実装と使用例を書いておく記事です。

例1 型が与えられたテンプレートから特殊化されているかを判断する

static_assert(is_specialized_from<std::vector<int>, std::vector>::value); // true
static_assert(!is_specialized_from<std::variant<int, std::string>, std::pair>::value); // false

実装

C++11の場合

template<class T, template<class...>class U>
struct is_specialized_from {
    template<class>
    struct make_void { using type = void; };
    template<class, class enabler = void>
    struct is_specialized_from_impl {
        static constexpr bool value = false;
    };

    template<template<class...>class T_Base, class... T_Args>
    struct is_specialized_from_impl<T_Base<T_Args...>, typename make_void<U<T_Args...>>::type> {
        static constexpr bool value = std::is_same<T_Base<T_Args...>, U<T_Args...>>::value;
    };

    static constexpr bool value = is_specialized_from_impl<T>::value;
};

C++20の場合

template<class T, template<class...>class U>
struct is_specialized_from : std::false_type{};

template<template<class...>class T_Base, class... T_Args, template<class...>class U>
requires std::same_as<T_Base<T_Args...>, U<T_Args...>>
struct is_specialized_from<T_Base<T_Args...>, U> : std::true_type{};

用例

関数をユニバーサル転送で書くことができる。

// ユニバーサル転送
template<class T>
requires is_specialized_from<std::remove_cvref_t<T>, std::vector>::value
void func(T&&);

// オーバーロードで解決しないといけない
template<class Value>
void func(std::vector<Value>&&);
template<typename Value>
void func(const std::vector<Value>&);

なお、std::arrayのような非型テンプレートが入っているものに対しては動かない。

例2 型特性テンプレートを型を引数にした関数オブジェクトのように扱う

std::tupleから与えられた型特性を満たす要素を取り出す

std::tuple<int, std::string, uint64_t>からstd::integralがtrueになる要素だけのtuple(std::tuple<int, uint64_t>)を作りたい。

実装

C++14以降


template<class Tup, template<class>class Filter>
struct tuple_filter_impl{
    template<class RemainIdxSeq, class ResultIdxSeq>
    struct extract_matched_index{ using type = ResultIdxSeq; };

    template<std::size_t IdxHead, std::size_t... IdxTails, std::size_t... ResultIdx>
    struct extract_matched_index<std::index_sequence<IdxHead, IdxTails...>, std::index_sequence<ResultIdx...>>{
        using type = std::conditional_t<Filter<std::tuple_element_t<IdxHead, Tup>>::value,
            typename extract_matched_index<std::index_sequence<IdxTails...>, std::index_sequence<ResultIdx..., IdxHead>>::type,
            typename extract_matched_index<std::index_sequence<IdxTails...>, std::index_sequence<ResultIdx...>>::type
        >;
    };

    using index_seq = typename extract_matched_index<std::make_index_sequence<std::tuple_size<Tup>::value>, std::index_sequence<>>::type;
    template<std::size_t... Is>
    static auto extract(const Tup& tup, std::index_sequence<Is...>) {
        return std::tuple<std::tuple_element_t<Is, Tup>...>{ std::get<Is>(tup)... };
    }
    template<std::size_t... Is>
    static auto extract(Tup&& tup, std::index_sequence<Is...>) {
        return std::tuple<std::tuple_element_t<Is, Tup>...>{ std::move(std::get<Is>(tup))... };
    }
};

template<template<class>class Filter, class T>
constexpr auto tuple_filter(T&& tup){
    using impl = tuple_filter_impl<std::remove_cv_t<std::remove_reference_t<T>>, Filter>;
    return impl::extract(std::forward<T>(tup), typename impl::index_seq{});
}

用例

const std::tuple<int, std::string, uint64_t> t;
tuple_filter<std::is_integral>(t) // std::tuple<int, uint64_t>

再帰&関数のオーバーロードで実行していた処理を簡潔に書ける。

struct Base{};
struct D1 : Base{
    void func() const { std::cout << "D1" << std::endl; }
};
struct D2 : Base{
    void func() const { std::cout << "D2" << std::endl; }
};

template<class T>
struct is_derived_from_Base : std::is_base_of<Base, std::remove_cv_t<std::remove_reference_t<T>>>{};

int main(){
    std::tuple<D1, std::string, D2> t;
    static_assert(std::is_same_v<decltype(tuple_filter<is_derived_from_Base>(t)), std::tuple<D1, D2>>);
    std::apply(
        [](auto&&... elms){ (elms.func(), ...); },
        tuple_filter<is_derived_from_Base>(t)
    );
}

いつ使うんだこれ?

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?