在元组的元组中找到一个元组
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。
我有一组 类,如 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。