我可以获得 return 类型的多个链式函数调用吗?
Can I get the return type of multiple chained functions calls?
我想将函数存储在一个有序的集合中,然后将它们全部应用于某个集合,这将导致获得大量修改的值,并将其存储在另一个集合中。我最初的尝试包括创建上述函数的 std::tuple
并尝试获取将所有这些函数应用于特定类型的结果类型 (std::invoke_result
):
int main() {
auto multiply = [](const auto arg){ return arg * arg; };
auto change = [](const auto arg){ return std::vector{arg}; };
auto to_string = [](const auto arg){ return arg.size() + " size"; };
auto functions = std::make_tuple(multiply, change, to_string);
std::vector<int> source{1, 2, 3, 4};
using f_type = decltype(functions);
using last_type =
std::tuple_element_t<std::tuple_size_v<f_type> - 1, f_type>;
using result_type =
std::invoke_result_t<last_type, /* size - 2 ret type and so on */>;
/*
* result_type is the type of applying *multiply* to int (type of *source*),
* then applying *change* to the result of *multiply* and then applying
* *to_string* to the result of *change*. Should be std::string.
*/
std::vector<result_type> results{};
}
问题是 std::invoke_result_t
的第二个 template
参数需要一个类型,该类型将传递给 last_type
类型对象的调用运算符。那需要减去最后一个元素的return类型之前的那个,等等(函数可能有很多)
我最终想要实现的是实现 Java 的流库(这个例子相当于链接 3 个 map
函数)。我还将保留额外的 enum
s,它将指示下一个元素是 map
、filter
还是任何其他受支持的函数,因此不会混淆该函数应该做什么 -现在的问题是开始使用这样的逻辑。
有没有办法获得链接任意数量函数的 return 类型,其中该类型传递给它已知的第一个函数?
或者我的设计缺陷太多,我宁愿按照完全不同的逻辑重新开始?
免责声明 - 我很清楚 C++20
(希望)rangesV3
即将推出。我试图模仿他们的行为(有一些小的变化)。我也知道 boost::adapters
- 他们的用法并不令我满意,另外我想尝试简单地实现类似的东西。
假设你有三个可调用对象 f g h,你想获取 h(g(f(args...)))
的类型,你可以这样做:
template <size_t first, class ChainedFns, class... Args>
decltype(auto) Call(ChainedFns &&fns, Args&&... args) {
if constexpr (std::tuple_size_v<std::decay_t<ChainedFns>> == 0)
return;
else if constexpr (first < std::tuple_size_v<std::decay_t<ChainedFns>>-1)
return Call<first + 1>(fns, std::invoke(std::get<first>(std::forward<ChainedFns>(fns)), std::forward<Args>(args)...));
else if constexpr (first == std::tuple_size_v<std::decay_t<ChainedFns>>-1)
return std::invoke(std::get<first>(std::forward<ChainedFns>(fns)), std::forward<Args>(args)...);
}
template <size_t first, class ChainedFns, class... Args>
struct invoke_result_of_chained_callables {
using type = decltype(Call<first>(std::declval<ChainedFns>(), std::declval<Args>()...));
};
template <size_t first, class ChainedFns, class... Args>
using invoke_result_of_chained_callables_t = typename invoke_result_of_chained_callables<first, ChainedFns, Args...>::type;
int main() {
auto fns = std::make_tuple(
[](auto) { return 0; }, // f
[](auto x) { return std::vector{ x }; }, // g
[](auto x) { return x.size(); } // h
);
using type = decltype(Call<0>(fns, nullptr));
static_assert(std::is_same_v<type, size_t>);
using type1 = invoke_result_of_chained_callables_t<0, decltype(fns), std::nullptr_t>;
static_assert(std::is_same_v<type, type1>);
return 0;
}
此代码片段也适用于任意数量的链式可调用对象。
我想将函数存储在一个有序的集合中,然后将它们全部应用于某个集合,这将导致获得大量修改的值,并将其存储在另一个集合中。我最初的尝试包括创建上述函数的 std::tuple
并尝试获取将所有这些函数应用于特定类型的结果类型 (std::invoke_result
):
int main() {
auto multiply = [](const auto arg){ return arg * arg; };
auto change = [](const auto arg){ return std::vector{arg}; };
auto to_string = [](const auto arg){ return arg.size() + " size"; };
auto functions = std::make_tuple(multiply, change, to_string);
std::vector<int> source{1, 2, 3, 4};
using f_type = decltype(functions);
using last_type =
std::tuple_element_t<std::tuple_size_v<f_type> - 1, f_type>;
using result_type =
std::invoke_result_t<last_type, /* size - 2 ret type and so on */>;
/*
* result_type is the type of applying *multiply* to int (type of *source*),
* then applying *change* to the result of *multiply* and then applying
* *to_string* to the result of *change*. Should be std::string.
*/
std::vector<result_type> results{};
}
问题是 std::invoke_result_t
的第二个 template
参数需要一个类型,该类型将传递给 last_type
类型对象的调用运算符。那需要减去最后一个元素的return类型之前的那个,等等(函数可能有很多)
我最终想要实现的是实现 Java 的流库(这个例子相当于链接 3 个 map
函数)。我还将保留额外的 enum
s,它将指示下一个元素是 map
、filter
还是任何其他受支持的函数,因此不会混淆该函数应该做什么 -现在的问题是开始使用这样的逻辑。
有没有办法获得链接任意数量函数的 return 类型,其中该类型传递给它已知的第一个函数?
或者我的设计缺陷太多,我宁愿按照完全不同的逻辑重新开始?
免责声明 - 我很清楚 C++20
(希望)rangesV3
即将推出。我试图模仿他们的行为(有一些小的变化)。我也知道 boost::adapters
- 他们的用法并不令我满意,另外我想尝试简单地实现类似的东西。
假设你有三个可调用对象 f g h,你想获取 h(g(f(args...)))
的类型,你可以这样做:
template <size_t first, class ChainedFns, class... Args>
decltype(auto) Call(ChainedFns &&fns, Args&&... args) {
if constexpr (std::tuple_size_v<std::decay_t<ChainedFns>> == 0)
return;
else if constexpr (first < std::tuple_size_v<std::decay_t<ChainedFns>>-1)
return Call<first + 1>(fns, std::invoke(std::get<first>(std::forward<ChainedFns>(fns)), std::forward<Args>(args)...));
else if constexpr (first == std::tuple_size_v<std::decay_t<ChainedFns>>-1)
return std::invoke(std::get<first>(std::forward<ChainedFns>(fns)), std::forward<Args>(args)...);
}
template <size_t first, class ChainedFns, class... Args>
struct invoke_result_of_chained_callables {
using type = decltype(Call<first>(std::declval<ChainedFns>(), std::declval<Args>()...));
};
template <size_t first, class ChainedFns, class... Args>
using invoke_result_of_chained_callables_t = typename invoke_result_of_chained_callables<first, ChainedFns, Args...>::type;
int main() {
auto fns = std::make_tuple(
[](auto) { return 0; }, // f
[](auto x) { return std::vector{ x }; }, // g
[](auto x) { return x.size(); } // h
);
using type = decltype(Call<0>(fns, nullptr));
static_assert(std::is_same_v<type, size_t>);
using type1 = invoke_result_of_chained_callables_t<0, decltype(fns), std::nullptr_t>;
static_assert(std::is_same_v<type, type1>);
return 0;
}
此代码片段也适用于任意数量的链式可调用对象。