仅当元组中存在该类型时才将函数应用于元组元素
Applying a function to tuple element only if that type is present in tuple
我想要实现的目标如下,连同我现在的位置:
template <typename Fn, typename Tuple, size_t... Is>
auto apply_if_impl(Tuple t, Fn&& f, std::index_sequence<Is...>) {
return std::make_tuple(
std::is_same_v<std::string, std::tuple_element_t<Is, Tuple>> ?
f(std::get<Is>(t)) :
std::get<Is>(t)...
);
}
template <typename Fn, typename ...Ts>
auto apply_if(std::tuple<Ts...> t, Fn&& f) {
return apply_if_impl(t, f, std::make_index_sequence<sizeof...(Ts)>());
}
并以如下方式实施,例如:
int main() {
std::tuple<int, std::string, float> t{42, "hello", 3.14f};
// this one should return
// std::tuple<int, std::size_t, float>{42, 5, 3.14f};
apply_if(t, [](std::string s){ return s.size(); });
// return value of this should be equal to t above since
// there is no vector element in the given tuple
apply_if(t, [](std::vector<int> s){ return s.size(); });
}
将 return 另一个 std::tuple
但 std::tuple<int, std::size_t, float>
具有元素 42 和 5("hello"
的长度)和 3.14。如果给定的元组中没有元素可以应用给定的可调用对象,则只 return 给定的元组而不做任何事情。因此,给定 std::tuple<int, std::string, float>
的副本
在后一种情况下将被 returned 或移动,无论如何。
我遇到的问题是,在我的三元语句中,编译器仍然看到该函数被应用于元组中的其他成员。我该如何解决这个问题?我需要一个编译时三元的东西,它可以正确扩展 make_tuple
调用。最后,我还需要摆脱那个硬编码的 std::string
。我需要在那里输入可调用的参数类型。
编辑:如果 boost::hana
这样的库会使解决方案更容易,请不要犹豫使用它们。对我来说也是一个很好的锻炼。
您可以通过另一个中间模板:
template <bool>
class Select
{
public:
template <typename F, typename T>
T& operator()(F&, T& t) const
{
return t;
}
};
template <>
class Select<true>
{
public:
template <typename F, typename T>
auto operator()(F& f, T& t) const -> decltype(f(t))
{
return f(t);
}
};
template<typename Fn, typename Tuple, size_t ... Is>
auto apply_if_impl(Tuple t, Fn&& f, std::index_sequence<Is...>)
{
return std::make_tuple
(
Select<std::is_same_v<std::string, std::tuple_element_t<Is, Tuple>>>()
(f, std::get<Is>(t))...
);
}
C++17 解决方案:
template<class Fn, typename Arg>
decltype(auto) apply_if_invocable(Fn fn, Arg&& arg) {
if constexpr (std::is_invocable_v<Fn, Arg>)
return fn(std::forward<Arg>(arg));
else
return std::forward<Arg>(arg);
}
template <typename Tuple, class Fn, size_t... Is>
auto apply_if_impl(Tuple&& t, Fn fn, std::index_sequence<Is...>) {
return std::make_tuple(apply_if_invocable(fn, std::get<Is>(std::forward<Tuple>(t)))...);
}
template <class Tuple, class Fn>
auto apply_if(Tuple&& t, Fn fn) {
return apply_if_impl(std::forward<Tuple>(t), fn,
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
}
然后:
struct Fn {
int operator()(int v) {
return 0;
}
std::size_t operator()(const std::string& v) {
return v.length();
}
};
std::tuple<int, std::string, std::unique_ptr<int>> t{
42, "hello", std::make_unique<int>(21)};
auto z = apply_if(std::move(t), Fn{});
assert(std::get<0>(z) == 0);
assert(std::get<1>(z) == 5);
assert(*std::get<2>(z) == 21);
我想要实现的目标如下,连同我现在的位置:
template <typename Fn, typename Tuple, size_t... Is>
auto apply_if_impl(Tuple t, Fn&& f, std::index_sequence<Is...>) {
return std::make_tuple(
std::is_same_v<std::string, std::tuple_element_t<Is, Tuple>> ?
f(std::get<Is>(t)) :
std::get<Is>(t)...
);
}
template <typename Fn, typename ...Ts>
auto apply_if(std::tuple<Ts...> t, Fn&& f) {
return apply_if_impl(t, f, std::make_index_sequence<sizeof...(Ts)>());
}
并以如下方式实施,例如:
int main() {
std::tuple<int, std::string, float> t{42, "hello", 3.14f};
// this one should return
// std::tuple<int, std::size_t, float>{42, 5, 3.14f};
apply_if(t, [](std::string s){ return s.size(); });
// return value of this should be equal to t above since
// there is no vector element in the given tuple
apply_if(t, [](std::vector<int> s){ return s.size(); });
}
将 return 另一个 std::tuple
但 std::tuple<int, std::size_t, float>
具有元素 42 和 5("hello"
的长度)和 3.14。如果给定的元组中没有元素可以应用给定的可调用对象,则只 return 给定的元组而不做任何事情。因此,给定 std::tuple<int, std::string, float>
的副本
在后一种情况下将被 returned 或移动,无论如何。
我遇到的问题是,在我的三元语句中,编译器仍然看到该函数被应用于元组中的其他成员。我该如何解决这个问题?我需要一个编译时三元的东西,它可以正确扩展 make_tuple
调用。最后,我还需要摆脱那个硬编码的 std::string
。我需要在那里输入可调用的参数类型。
编辑:如果 boost::hana
这样的库会使解决方案更容易,请不要犹豫使用它们。对我来说也是一个很好的锻炼。
您可以通过另一个中间模板:
template <bool>
class Select
{
public:
template <typename F, typename T>
T& operator()(F&, T& t) const
{
return t;
}
};
template <>
class Select<true>
{
public:
template <typename F, typename T>
auto operator()(F& f, T& t) const -> decltype(f(t))
{
return f(t);
}
};
template<typename Fn, typename Tuple, size_t ... Is>
auto apply_if_impl(Tuple t, Fn&& f, std::index_sequence<Is...>)
{
return std::make_tuple
(
Select<std::is_same_v<std::string, std::tuple_element_t<Is, Tuple>>>()
(f, std::get<Is>(t))...
);
}
C++17 解决方案:
template<class Fn, typename Arg>
decltype(auto) apply_if_invocable(Fn fn, Arg&& arg) {
if constexpr (std::is_invocable_v<Fn, Arg>)
return fn(std::forward<Arg>(arg));
else
return std::forward<Arg>(arg);
}
template <typename Tuple, class Fn, size_t... Is>
auto apply_if_impl(Tuple&& t, Fn fn, std::index_sequence<Is...>) {
return std::make_tuple(apply_if_invocable(fn, std::get<Is>(std::forward<Tuple>(t)))...);
}
template <class Tuple, class Fn>
auto apply_if(Tuple&& t, Fn fn) {
return apply_if_impl(std::forward<Tuple>(t), fn,
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
}
然后:
struct Fn {
int operator()(int v) {
return 0;
}
std::size_t operator()(const std::string& v) {
return v.length();
}
};
std::tuple<int, std::string, std::unique_ptr<int>> t{
42, "hello", std::make_unique<int>(21)};
auto z = apply_if(std::move(t), Fn{});
assert(std::get<0>(z) == 0);
assert(std::get<1>(z) == 5);
assert(*std::get<2>(z) == 21);