在元组的元组中找到一个元组

find a tuple in a tuple of tuples

我有一组 类,如 A、B、C 和包含这些 类 的元组的元组,如下所示:

struct A {
    std::string name{"a"};
};

struct B {
    std::string name{"b"};
};

struct C {
    std::string name{"c"};
};

// only first items A(), B(), C() do matter, other are arbitrary
auto t = std::make_tuple(
    std::make_tuple(A(), 1, 2, 3),
    std::make_tuple(B(), 4, 5, 6),
    std::make_tuple(C(), 7, 8)
);

我的目标逻辑是 select 通过匹配第一个元素的类型从容器元组中生成一个元组。所以,通过上面的例子,我想在调用这样的东西时得到字符串 'b':

std::string the_b = having_first_of_type<B, decltype(t)>::get().name;

我正在尝试使用模板获得解决方案:

// a template for getting first item from N-th tuple int Tuples
template <std::size_t N, typename... Tuples>
using first_of_nth = std::tuple_element<0, std::tuple_element<N, std::tuple<Tuples...>>>;

template <std::size_t N, class T, class... Tuples>
struct having_first_of_type;

template <std::size_t N, class T, class... Tuples>
struct having_first_of_type<N,
                   typename std::enable_if<std::is_same<T, typename first_of_nth<N, Tuples...>::type>::value, T>::type* = nullptr>
{
    static auto& get(const std::tuple<Tuples...>& tuple) {
        return std::get<N>(tuple);
    }
};

template <std::size_t N, class T, class... Tuples>
struct having_first_of_type<N,
                   typename std::enable_if<!std::is_same<T, typename first_of_nth<N, Tuples...>::type>::value, T>::type* = nullptr> : having_first_of_type<N-1, T, Tuples...>;

template <std::size_t N, class T, class... Tuples>
struct having_first_of_type<0, T, Tuples...> {}

而且我无法以正确的方式形成专业化。对于第一个(std::is_same 为真)编译器说:error: expected '>' 表示'= nullptr' 的位置。看起来它不接受 T* 的默认值,但我很困惑为什么..

错误是什么?或者,也许有更好的方法可以得到我想要的东西?

更新 以下是 2 个可行的解决方案:来自 N. Shead 和@n314159 - 谢谢!

我忘了说我尝试使用 C++14 来获取它,但解决方案是针对 C++17 的。

C++17也行

您已尝试在编译器需要具体类型的地方提供默认值。

我假设你想获得整个内部元组? 在那种情况下,我解决这个问题的尝试看起来像这样:

template <typename T, typename Tuple>
constexpr bool tuple_first_type_is() {
    if constexpr (std::tuple_size_v<Tuple> == 0)
        return false;
    else
        return std::is_same_v<T, std::tuple_element_t<0, Tuple>>;
}

template <typename T, std::size_t I, typename NestedTuple>
constexpr decltype(auto) having_first_of_type_impl(NestedTuple&& nested_tuple) noexcept {
    using D = std::decay_t<NestedTuple>;
    static_assert(I < std::tuple_size_v<D>, "type not found in tuple");
    using ith_tuple = std::tuple_element_t<I, D>;

    if constexpr (tuple_first_type_is<T, ith_tuple>())
        return std::get<I>(std::forward<NestedTuple>(nested_tuple));
    else
        return having_first_of_type_impl<T, I+1>(std::forward<NestedTuple>(nested_tuple));
}

template <typename T, typename NestedTuple>
constexpr decltype(auto) having_first_of_type(NestedTuple&& nested_tuple) noexcept {
    static_assert(std::tuple_size_v<std::decay_t<NestedTuple>> > 0, "empty tuple");
    return having_first_of_type_impl<T, 0>(std::forward<NestedTuple>(nested_tuple));
}

直播:http://coliru.stacked-crooked.com/a/aa1637939a5d7d7c

我不是 100% 有信心我已经正确地完成了值类别等的所有事情,很可能有更好的方法来解决这个问题,但这是我会开始的事情.

在样式所属的位置分配空指针没有意义。你应该删除它。此外,我不太确定出了什么问题。我们可以通过使用 std::get 模板化的类型而不是索引来使整个事情变得更容易一些,然后我们就不必携带 N:

#include <tuple>
#include <type_traits>
#include <iostream>

template<class T, class Tup, bool negated = false>
using first_of = std::enable_if_t<negated ^ std::is_same_v<std::tuple_element_t<0, Tup>, T>>;

template<class T, class= void, class... Tups>
struct first_match_impl;

template<class T, class Tup1, class... Tups> 
struct first_match_impl<T, first_of<T, Tup1>, Tup1, Tups...> {
    using type = Tup1;

    template<class FullTup>
    static Tup1& get(FullTup& t) {
        return std::get<Tup1>(t);
    }
};

template<class T, class Tup1, class... Tups>
struct first_match_impl<T, first_of<T, Tup1, true>, Tup1, Tups...>: first_match_impl<T, void, Tups...> {};

template<class T, class... Tups>
using first_match = first_match_impl<T, void, Tups...>;

template<class T, class... Tups>
auto& get_first_of(std::tuple<Tups...> &t) {
    return first_match<T, Tups...>::get(t);
}

int main()
{
    std::tuple<std::tuple<int, float>, std::tuple<char, double>> t {{1,2.}, {'A', 4.}};
    std::cout << std::get<0>(get_first_of<char>(t)); // prints A
}

请注意,当你的元组中有两个完全相同的元组时,这将不会编译,但如果有不同的元组具有相同的第一个元素(然后它将选择其中的第一个),则会编译。

编辑: 这启发我编写了一个小型库,提供类似迭代器的元组支持。参见 here