簡體   English   中英

有條件地刪除折疊表達式中的 function 調用

[英]Conditionally remove function calls in fold expression

我知道我可以使用 SFINAE 根據條件禁用模板化函數的生成,但這在這種情況下實際上不起作用。 我想在編譯時初始化一個數組,該數組應包含與條件匹配的值。 像這樣的東西:

template <std::size_t i, class ... Types, class ... Group>
constexpr auto fetch_match(const std::tuple<Group...>& candidates)
{
    if constexpr (is_match<std::tuple<Group...>, i, Types...>())
    {
        auto& group = std::get<i>(candidates);
        return group.template get<Types...>();
    }
}

template <class ... Types, class ... Group, std::size_t ... indices>
constexpr auto get_matches(const std::tuple<Group...>& candidates, std::index_sequence<indices...>)
{
    constexpr std::array views {
        (fetch_match<indices, Types...>(candidates), ...),
    };
    return views;
}

我知道上面的代碼是錯誤的,不能編譯。 如果未滿足條件,那么我希望折疊表達式不生成該 function 調用。 我該怎么做?


這個問題可能是一個 XY 問題,所以這里有一個更詳細的問題。

我有一個包含Group異構數據的Registry 我希望能夠查詢包含指定子類型列表的所有組。 例如, for (const auto& view: registry.get<char, short, int>())應該產生一個數組,其中包含包含charshortint的組的視圖。 我在下面創建了一個mcve 當前代碼的問題是我必須首先創建數組然后復制視圖,我想避免這種情況。

#include <tuple>
#include <array>
#include <utility>
#include <type_traits>
#include <iostream>


template <typename T, typename... Ts>
constexpr bool contains = (std::is_same<T, Ts>{} || ...);

template <typename Subset, typename Set>
constexpr bool is_subset_of = false;

template <typename... Ts, typename... Us>
constexpr bool is_subset_of<std::tuple<Ts...>, std::tuple<Us...>> = (contains<Ts, Us...> && ...);

template <typename ... T>
struct View
{
    const char* name_of_group;  // For debugging.
    std::tuple<T...> data;
};

template <typename ... Ts>
struct Group
{
    using type_set = std::tuple<Ts...>;
    static const char* name;   // For debugging.

    std::tuple<Ts...> data;

    explicit Group(Ts... values) : data(values...) {}

    template <typename ... Us>
    [[nodiscard]] View<Us...> get() const noexcept
    {
        return { this->name, std::make_tuple(std::get<Us>(this->data)...) };
    }
};

template <class Groups, std::size_t i, class ... Types>
constexpr bool is_match()
{
    using group_type = std::tuple_element_t<i, Groups>;
    bool match = is_subset_of<std::tuple<Types...>, typename group_type::type_set>;
    return match;
}


template <std::size_t i, class ... Types, class ... Group, class Array>
constexpr void add_matches(const std::tuple<Group...>& candidates, Array& matches, std::size_t& index)
{
    if constexpr (is_match<std::tuple<Group...>, i, Types...>())
    {
        auto& group = std::get<i>(candidates);
        matches[index++] = group.template get<Types...>();
    }
}

template <class ... Types, class ... Group, std::size_t ... indices>
constexpr auto get_matches(const std::tuple<Group...>& candidates, std::index_sequence<indices...>)
{
    constexpr std::size_t size = (is_match<std::tuple<Group...>, indices, Types...>() + ... + 0);
    std::array<View<Types...>, size> views {};
    std::size_t index = 0;
    (add_matches<indices, Types...>(candidates, views, index), ...);
    return views;
}


template <typename ... Group>
class Registry
{
public:
    explicit Registry(Group... groups) : groups(groups...) {}

    template <typename ... T>
    auto get()
    {
        constexpr auto indices = std::index_sequence_for<Group...>{};
        return get_matches<T...>(this->groups, indices);
    }

private:
    std::tuple<Group...> groups;
};


using A = Group<char>;
using B = Group<char, short>;
using C = Group<char, short, int>;
using D = Group<char, short, int, long long>;

// Giving the classes names for debugging purposes.
template<> const char* A::name = "A";
template<> const char* B::name = "B";
template<> const char* C::name = "C";
template<> const char* D::name = "D";


int main()
{
    auto registry = Registry(A{0}, B{1,1}, C{2,2,2}, D{3,3,3,3});

    // Should yield an array of size 2 with View<char, short, int>,
    // one from group C and one from Group D.
    for (const auto& view : registry.get<char, short, int>())
    {
        std::cout << "View of group: " << view.name_of_group     << std::endl;
        std::cout << "char: "  << int(std::get<char>(view.data)) << std::endl;
        std::cout << "short: " << std::get<short>(view.data)     << std::endl;
        std::cout << "int: "   << std::get<int>(view.data)       << std::endl;
    }
}

嘗試評論中的建議,以下代碼是我所得到的。

template <class Groups, std::size_t i, class ... Types>
constexpr bool is_match()
{
    using group_type = std::tuple_element_t<i, Groups>;
    bool match = is_subset_of<std::tuple<Types...>, typename group_type::type_set>;
    return match;
}
template <class ... Types, class ... Group, std::size_t ... indices>
constexpr auto build_view_array(const std::tuple<Group...>& candidates, std::index_sequence<indices...>)
{
    std::array views {
            std::get<indices>(candidates).template get<Types...>()...
    };
    return views;
}
template <std::size_t i, class Groups, class TypeSet, std::size_t ... x>
constexpr auto get_matching_indices()
{
    if constexpr (is_match<Groups, i, TypeSet>())
        return std::index_sequence<x..., i>{};
    else
        return std::index_sequence<x...>{};
}
template <std::size_t i, std::size_t j, std::size_t ... rest, class Groups, class TypeSet, std::size_t ... x>
constexpr auto get_matching_indices()
{
    if constexpr (is_match<Groups, i, TypeSet>())
        return get_matching_indices<j, rest..., Groups, TypeSet, i, x...>();
    else
        return get_matching_indices<j, rest..., Groups, TypeSet, x...>();
}

template <class ... Types, class ... Group, std::size_t ... indices>
constexpr auto get_matches(const std::tuple<Group...>& candidates, std::index_sequence<indices...>)
{
    constexpr auto matching_indices = get_matching_indices<indices..., std::tuple<Group...>, std::tuple<Types...>>();
    constexpr auto views = build_view_array<Types...>(candidates, matching_indices);
    return views;
}

感覺它應該可以工作,但由於以下錯誤而無法編譯:

/Users/tedkleinbergman/Programming/ECS/temp.cpp:76:39: error: no matching function for call to 'get_matching_indices'
    constexpr auto matching_indices = get_matching_indices<indices..., std::tuple<Group...>, std::tuple<Types...>>();
                                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/tedkleinbergman/Programming/ECS/temp.cpp:92:16: note: in instantiation of function template specialization 'get_matches<char, short, int, Group<char>, Group<char, short>, Group<char, short, int>, Group<char, short, int, long long> , 0, 1, 2, 3>' requested here
        return get_matches<T...>(this->groups, indices);
               ^
/Users/tedkleinbergman/Programming/ECS/temp.cpp:118:38: note: in instantiation of function template specialization 'Registry<Group<char>, Group<char, short>, Group<char, short, int>, Group<char, short, int, long long> >::get<char, short, int>' requested here
    for (const auto& view : registry.get<char, short, int>())
                                     ^
/Users/tedkleinbergman/Programming/ECS/temp.cpp:57:16: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'Groups'
constexpr auto get_matching_indices()
               ^
/Users/tedkleinbergman/Programming/ECS/temp.cpp:65:16: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'rest'
constexpr auto get_matching_indices()
               ^
1 error generated.

首先,從index_sequence過濾器開始:

template<std::size_t I>
using index_t = std::integral_constant<std::size_t, I>;
template<std::size_t I>
constexpr index_t<I> index = {};

template<std::size_t...Is, std::size_t...Js>
constexpr std::index_sequence<Is...,Js...> concatenate( std::index_sequence<Is...>, std::index_sequence<Js...> ) {
  return {};
}
template <class Test>
constexpr auto filter_sequence(std::index_sequence<> sequence, Test test) {
  return sequence;
}

template<std::size_t I0, std::size_t...Is, class Test>
constexpr auto filter_sequence( std::index_sequence<I0, Is...>, Test test )
{
  constexpr auto tail = filter_sequence( std::index_sequence<Is...>{}, test );
  if constexpr ( test(index<I0>) ) {
    return concatenate( std::index_sequence<I0>{}, tail );
  } else {
    return tail;
  }
}

然后我們使用這些原語。

template <class Group, class ... Types>
constexpr auto get_match_indexes()
{
  constexpr auto test = [](auto I){ return is_match<Group, I, Types...>(); };
  constexpr auto indexes = std::make_index_sequence< std::tuple_size_v<Group> >{};
  constexpr auto retval = filter_sequence( indexes, test );
  return retval;
}
template<class ... Types, class Group, std::size_t...Is>
std::array<sizeof...Is, View<Types...>> get_matches(const Group& candidates, std::index_sequence<Is...> ) {
  return {{
    std::get<Is>(candidates).template get<Types...>(), ...
  }};
}
template<class ... Types, class Group>
std::array<sizeof...Is, View<Types...>> get_matches(const Group& candidates ) {
  return get_matches<Types...>( candidates, get_match_indexes<Group, Types...>() );
}

或類似的東西。

請注意,某些編譯器可能需要將is_match<Group, I, Types...>()替換為is_match<Group, decltype(I)::value, Types...>()

可能有錯別字。 這至少使用

filter_sequence使用 O(n^2) 模板符號長度和 O(n) 遞歸模板實例化深度。 可以通過復雜的代碼將其改進為 O(n lg n) 長度和 O(lg n) 深度; 基本上,您需要將Is...拆分為As...Bs...在中間並以這種方式遞歸。

這是索引序列的對數深度拆分:

template<class A, class B>
struct two_things {
  A a;
  B b;
};
template<class A, class B>
two_things(A,B)->two_things<A,B>;
    
template<class Seq>
constexpr auto split_sequence( index_t<0>, Seq seq ) {
  return two_things{ std::index_sequence<>{}, seq };
}

template<std::size_t I0, std::size_t...Is>
constexpr auto split_sequence( index_t<1>, std::index_sequence<I0, Is...> seq ) {
  return two_things{ std::index_sequence<I0>{}, std::index_sequence<Is...>{} };
}

template<std::size_t N, class Seq>
constexpr auto split_sequence( index_t<N>, Seq seq ) {
  constexpr auto step1 = split_sequence( constexpr_index<N/2>, seq );
  constexpr auto step2 = split_sequence( constexpr_index<N-N/2>, step1.b );
  return two_things{ concatenate(step1.a, step2.a), step2.b };
}


template<std::size_t...Is>
constexpr auto halve_sequence( std::index_sequence<Is...> seq ) {
  return split( index< (sizeof...(Is)) / 2u >, seq );
}

two_things作為比標准元組或對輕許多倍的元組或對存在)。

這反過來又可以讓您改進過濾器順序。

template<std::size_t I, class Test>
constexpr auto filter_sequence( std::index_sequence<I> seq, Test test )
{
  if constexpr ( test(constexpr_index<I>) ) {
    return seq;
  } else {
    return std::index_sequence<>{};
  }
}

template<std::size_t...Is, class Test>
constexpr auto filter_sequence( std::index_sequence<Is...> seq, Test test )
{
  constexpr auto split = halve_sequence( seq );
  constexpr auto head = filter_sequence( split.a, test );
  constexpr auto tail = filter_sequence( split.b, test );
  return concatenate(head, tail);
}

這個版本應該編譯得更快並且使用更少的 memory,特別是對於大量元素。 但是你應該從上面更簡單的開始,因為(正如我所指出的)可能有很多 tpyos。

活生生的例子

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM