如何扩展 std::apply 以处理非元组类型?
How to extend std::apply to work on non tuple types?
我有一个案例,我需要将输入参数应用于函数而不关心它是否是元组。如果是元组,需要解包,所以不需要函数参数检测
这是我试过的:
template <typename Callable, typename Tuple>
auto geniune_apply(Callable&& callable, Tuple&& tuple)
{
return std::apply(std::forward<Callable>(callable), std::forward<Tuple>(tuple));
}
template <typename Callable, typename T, typename = typename std::enable_if<!shino::is_tuple_like<std::decay_t<T>>::value>::type>
auto geniune_apply(Callable&& callable, T&& arg)
{
return std::forward<Callable>(callable)(std::forward<T>(arg));
}
它导致歧义,这是我所期望的。然后我尝试使用 SFINAE 代替元组的大小,但我无法防止非元组类型的编译错误。
这是我正在使用的测试用例:
#include <cassert>
#include <iostream>
#include <stdexcept>
#include <vector>
int dummy_x(const std::tuple<int, int>&)
{
return 1;
}
int dummy_y(int y)
{
return y;
}
int main()
{
shino::geniune_apply(&dummy_x, std::tuple<int, int>(1, 1));
shino::geniune_apply(dummy_y, 1);
shino::geniune_apply(dummy_y, std::make_tuple(1));
}
如果需要,类似元组的代码。它基本上测试它是 std::array
还是 std::tuple
:
template <typename T>
struct is_straight_tuple
{
static constexpr bool value = false;
constexpr operator bool()
{
return value;
}
};
template <typename ... Ts>
struct is_straight_tuple<std::tuple<Ts...>>
{
static constexpr bool value = true;
constexpr operator bool()
{
return value;
}
};
template <typename T>
struct is_std_array
{
static constexpr bool value = false;
};
template <typename T, std::size_t size>
struct is_std_array<std::array<T, size>>
{
static constexpr bool value = true;
constexpr operator bool()
{
return value;
}
};
template <typename T>
struct is_tuple_like
{
static constexpr bool value = is_std_array<T>::value || is_straight_tuple<T>::value;
constexpr operator bool()
{
return value;
}
};
在 C++14 中解决这个问题的最简单方法是使用标记调度:
template <typename Callable, typename Arg>
decltype(auto) geniune_apply(Callable&& callable, Arg&& arg)
{
return details::genuine_apply(std::forward<Callable>(callable),
std::forward<Arg>(arg),
is_tuple_like<std::decay_t<Arg>>{});
}
将您的 is_tuple_like
更改为从 std::integral_constant<bool, ???>
继承,而不是重新实现它。这将允许您编写这两个辅助函数:
namespace details {
// the tuple-like case
template <typename Callable, typename Tuple>
decltype(auto) genuine_apply(Callable&&, Tuple&&, std::true_type );
// the non-tuple-like case
template <typename Callable, typename Arg>
decltype(auto) genuine_apply(Callable&&, Arg&&, std::false_type );
}
在 C++17 中,更好的解决方案是简单地使用 if constexpr
而不是标签调度。使用 C++ 概念,您最初解决问题的方法实际上可以按原样工作(有一个不受约束的函数模板,另一个受第二个参数约束,类似于元组)。
我有一个案例,我需要将输入参数应用于函数而不关心它是否是元组。如果是元组,需要解包,所以不需要函数参数检测
这是我试过的:
template <typename Callable, typename Tuple>
auto geniune_apply(Callable&& callable, Tuple&& tuple)
{
return std::apply(std::forward<Callable>(callable), std::forward<Tuple>(tuple));
}
template <typename Callable, typename T, typename = typename std::enable_if<!shino::is_tuple_like<std::decay_t<T>>::value>::type>
auto geniune_apply(Callable&& callable, T&& arg)
{
return std::forward<Callable>(callable)(std::forward<T>(arg));
}
它导致歧义,这是我所期望的。然后我尝试使用 SFINAE 代替元组的大小,但我无法防止非元组类型的编译错误。
这是我正在使用的测试用例:
#include <cassert>
#include <iostream>
#include <stdexcept>
#include <vector>
int dummy_x(const std::tuple<int, int>&)
{
return 1;
}
int dummy_y(int y)
{
return y;
}
int main()
{
shino::geniune_apply(&dummy_x, std::tuple<int, int>(1, 1));
shino::geniune_apply(dummy_y, 1);
shino::geniune_apply(dummy_y, std::make_tuple(1));
}
如果需要,类似元组的代码。它基本上测试它是 std::array
还是 std::tuple
:
template <typename T>
struct is_straight_tuple
{
static constexpr bool value = false;
constexpr operator bool()
{
return value;
}
};
template <typename ... Ts>
struct is_straight_tuple<std::tuple<Ts...>>
{
static constexpr bool value = true;
constexpr operator bool()
{
return value;
}
};
template <typename T>
struct is_std_array
{
static constexpr bool value = false;
};
template <typename T, std::size_t size>
struct is_std_array<std::array<T, size>>
{
static constexpr bool value = true;
constexpr operator bool()
{
return value;
}
};
template <typename T>
struct is_tuple_like
{
static constexpr bool value = is_std_array<T>::value || is_straight_tuple<T>::value;
constexpr operator bool()
{
return value;
}
};
在 C++14 中解决这个问题的最简单方法是使用标记调度:
template <typename Callable, typename Arg>
decltype(auto) geniune_apply(Callable&& callable, Arg&& arg)
{
return details::genuine_apply(std::forward<Callable>(callable),
std::forward<Arg>(arg),
is_tuple_like<std::decay_t<Arg>>{});
}
将您的 is_tuple_like
更改为从 std::integral_constant<bool, ???>
继承,而不是重新实现它。这将允许您编写这两个辅助函数:
namespace details {
// the tuple-like case
template <typename Callable, typename Tuple>
decltype(auto) genuine_apply(Callable&&, Tuple&&, std::true_type );
// the non-tuple-like case
template <typename Callable, typename Arg>
decltype(auto) genuine_apply(Callable&&, Arg&&, std::false_type );
}
在 C++17 中,更好的解决方案是简单地使用 if constexpr
而不是标签调度。使用 C++ 概念,您最初解决问题的方法实际上可以按原样工作(有一个不受约束的函数模板,另一个受第二个参数约束,类似于元组)。