一个recursive_invoke_result_t模板结构
A recursive_invoke_result_t template structure
我正在尝试使用 C++20 概念实现递归版本 std::invoke_result_t
,以便可以检索嵌套调用结果的类型。
recursive_invoke_result_t
的用例
auto f1 = [](int x) { return static_cast<double>(x); };
std::cout << typeid(recursive_invoke_result_t<decltype(f1), int>).name() << std::endl; // expected output "double"
std::cout << typeid(recursive_invoke_result_t<decltype(f1), std::vector<int>>).name() << std::endl; // expected output is something like "std::vector<double>"
std::cout << typeid(recursive_invoke_result_t<decltype(f1), std::vector<std::vector<int>>>).name() << std::endl; // expected output is something like "std::vector<std::vector<double>>"
auto f2 = [](int x) { return std::to_string(x); };
std::cout << typeid(recursive_invoke_result_t<decltype(f2), std::vector<int>>).name() << std::endl; // expected output is something like "std::vector<string>"
auto f3 = [](std::string x) { return std::atoi(x.c_str()); };
std::cout << typeid(recursive_invoke_result_t<decltype(f3), std::vector<std::string>>).name() << std::endl; // expected output is something like "std::vector<int>"
实验实现
实验实现如下
// recursive_invoke_result_t implementation
template<typename F, typename T>
requires (std::invocable<F, T>)
struct recursive_invoke_result_t_detail
{
using type = std::invoke_result_t<F, T>;
};
template <typename F, template <typename...> typename Container, typename... Ts>
requires (std::ranges::input_range<std::ranges::range_value_t<Container<Ts...>>>) && (!std::invocable<F, Container<Ts...>>)
struct recursive_invoke_result_t_detail<F, Container<Ts...>>
{
using type = Container<typename recursive_invoke_result_t_detail<F, std::iter_value_t<Container<Ts...>>>::type>;
};
template<typename F, typename T>
using recursive_invoke_result_t = typename recursive_invoke_result_t_detail<F, T>::type;
在上面的实验实现中,测试用例recursive_invoke_result_t<decltype(f1), int>
似乎工作正常。当涉及到第二个测试用例recursive_invoke_result_t<decltype(f1), std::vector<int>>
时,出现了编译错误'recursive_invoke_result_t_detail': the associated constraints are not satisfied
、'recursive_invoke_result_t' : Failed to specialize alias template
和unable to recover from previous error(s); stopping compilation
。有什么办法可以解决这个问题吗?请给我一些提示或例子。
这个
template<typename F, typename T> requires std::invocable<F, T>
struct recursive_invoke_result_t_detail
{
using type = std::invoke_result_t<F, T>;
};
是 recursive_invoke_result_t_detail
的 主要 模板。 requires std::invocable<F, T>
则意味着 recursive_invoke_result_t_detail<F, T>
仅在 std::invocable<F, T>
时有效。时期。任何专业化都不能放松这个要求;它是模板本身的 属性。对特化的 requires
约束只是一个附加约束:如果 std::invocable<F, T>
,则模板只能用 <F, T>
实例化,并且仅当进一步 T = Container<Ts...>
时,部分特化才适用对于某些 Ts...
并且您提供的其他约束得到满足(这实际上是不可能的!)
让主模板不受约束(并根据约定为其命名和别名)
template<typename, typename>
struct recursive_invoke_result { };
template<typename F, typename T>
using recursive_invoke_result_t = recursive_invoke_result<F, T>::type;
为基本案例提供部分专业化
template<typename T, std::invocable<T> F>
struct recursive_invoke_result<F, T> { using type = std::invoke_result_t<F, T>; };
然后是你的其他部分专业化
template<typename F, template<typename...> typename Container, typename... Ts>
requires (
!std::invocable<F, Container<Ts...>> && // don't conflict with base case
std::ranges::input_range<Container<Ts...>> && // no idea why you asked for the contained type to be range, fixed it
requires { typename recursive_invoke_result_t<F, std::ranges::range_value_t<Container<Ts...>>>; }) // SFINAE-compatibility
struct recursive_invoke_result<F, Container<Ts...>> {
using type = Container<recursive_invoke_result_t<F, std::ranges::range_value_t<Container<Ts...>>>>; // not iter_value_t, the container isn't an iterator!
};
好吃
// typeid(...).name() doesn't produce anything readable for me on Godbolt
template<typename T>
std::ostream &print_type(std::ostream &out) {
// stealing
// works only for GCC
std::string_view name = __PRETTY_FUNCTION__;
name.remove_prefix(50);
name.remove_suffix(42);
return out << name;
};
template<typename T>
constexpr void assert_no_type() { }
template<typename T> requires requires { typename T::type; }
void assert_no_type() = delete;
int main() {
using F = decltype([](int x) -> double { return x; });
std::cout << "double(int), int: " << print_type<recursive_invoke_result_t<F, int>> << "\n";
std::cout << "double(int), std::vector<int>: " << print_type<recursive_invoke_result_t<F, std::vector<int>>> << "\n";
assert_no_type<recursive_invoke_result<F, std::vector<std::ostream>>>();
std::cout << "double(int), std::vector<std::vector<int>>: " << print_type<recursive_invoke_result_t<F, std::vector<std::vector<int>>>> << "\n";
std::cout << "double(int), std::vector<std::set<int>>: " << print_type<recursive_invoke_result_t<F, std::vector<std::set<int>>>> << "\n";
using G = decltype([](std::vector<int> const &v) -> std::vector<double> { return {}; });
assert_no_type<recursive_invoke_result<G, std::set<std::set<int>>>>();
std::cout << "std::vector<double>(std::vector<int>): " << print_type<recursive_invoke_result_t<G, std::set<std::vector<int>>>> << "\n";
}
我正在尝试使用 C++20 概念实现递归版本 std::invoke_result_t
,以便可以检索嵌套调用结果的类型。
recursive_invoke_result_t
auto f1 = [](int x) { return static_cast<double>(x); };
std::cout << typeid(recursive_invoke_result_t<decltype(f1), int>).name() << std::endl; // expected output "double"
std::cout << typeid(recursive_invoke_result_t<decltype(f1), std::vector<int>>).name() << std::endl; // expected output is something like "std::vector<double>"
std::cout << typeid(recursive_invoke_result_t<decltype(f1), std::vector<std::vector<int>>>).name() << std::endl; // expected output is something like "std::vector<std::vector<double>>"
auto f2 = [](int x) { return std::to_string(x); };
std::cout << typeid(recursive_invoke_result_t<decltype(f2), std::vector<int>>).name() << std::endl; // expected output is something like "std::vector<string>"
auto f3 = [](std::string x) { return std::atoi(x.c_str()); };
std::cout << typeid(recursive_invoke_result_t<decltype(f3), std::vector<std::string>>).name() << std::endl; // expected output is something like "std::vector<int>"
实验实现
实验实现如下
// recursive_invoke_result_t implementation
template<typename F, typename T>
requires (std::invocable<F, T>)
struct recursive_invoke_result_t_detail
{
using type = std::invoke_result_t<F, T>;
};
template <typename F, template <typename...> typename Container, typename... Ts>
requires (std::ranges::input_range<std::ranges::range_value_t<Container<Ts...>>>) && (!std::invocable<F, Container<Ts...>>)
struct recursive_invoke_result_t_detail<F, Container<Ts...>>
{
using type = Container<typename recursive_invoke_result_t_detail<F, std::iter_value_t<Container<Ts...>>>::type>;
};
template<typename F, typename T>
using recursive_invoke_result_t = typename recursive_invoke_result_t_detail<F, T>::type;
在上面的实验实现中,测试用例recursive_invoke_result_t<decltype(f1), int>
似乎工作正常。当涉及到第二个测试用例recursive_invoke_result_t<decltype(f1), std::vector<int>>
时,出现了编译错误'recursive_invoke_result_t_detail': the associated constraints are not satisfied
、'recursive_invoke_result_t' : Failed to specialize alias template
和unable to recover from previous error(s); stopping compilation
。有什么办法可以解决这个问题吗?请给我一些提示或例子。
这个
template<typename F, typename T> requires std::invocable<F, T>
struct recursive_invoke_result_t_detail
{
using type = std::invoke_result_t<F, T>;
};
是 recursive_invoke_result_t_detail
的 主要 模板。 requires std::invocable<F, T>
则意味着 recursive_invoke_result_t_detail<F, T>
仅在 std::invocable<F, T>
时有效。时期。任何专业化都不能放松这个要求;它是模板本身的 属性。对特化的 requires
约束只是一个附加约束:如果 std::invocable<F, T>
,则模板只能用 <F, T>
实例化,并且仅当进一步 T = Container<Ts...>
时,部分特化才适用对于某些 Ts...
并且您提供的其他约束得到满足(这实际上是不可能的!)
让主模板不受约束(并根据约定为其命名和别名)
template<typename, typename>
struct recursive_invoke_result { };
template<typename F, typename T>
using recursive_invoke_result_t = recursive_invoke_result<F, T>::type;
为基本案例提供部分专业化
template<typename T, std::invocable<T> F>
struct recursive_invoke_result<F, T> { using type = std::invoke_result_t<F, T>; };
然后是你的其他部分专业化
template<typename F, template<typename...> typename Container, typename... Ts>
requires (
!std::invocable<F, Container<Ts...>> && // don't conflict with base case
std::ranges::input_range<Container<Ts...>> && // no idea why you asked for the contained type to be range, fixed it
requires { typename recursive_invoke_result_t<F, std::ranges::range_value_t<Container<Ts...>>>; }) // SFINAE-compatibility
struct recursive_invoke_result<F, Container<Ts...>> {
using type = Container<recursive_invoke_result_t<F, std::ranges::range_value_t<Container<Ts...>>>>; // not iter_value_t, the container isn't an iterator!
};
好吃
// typeid(...).name() doesn't produce anything readable for me on Godbolt
template<typename T>
std::ostream &print_type(std::ostream &out) {
// stealing
// works only for GCC
std::string_view name = __PRETTY_FUNCTION__;
name.remove_prefix(50);
name.remove_suffix(42);
return out << name;
};
template<typename T>
constexpr void assert_no_type() { }
template<typename T> requires requires { typename T::type; }
void assert_no_type() = delete;
int main() {
using F = decltype([](int x) -> double { return x; });
std::cout << "double(int), int: " << print_type<recursive_invoke_result_t<F, int>> << "\n";
std::cout << "double(int), std::vector<int>: " << print_type<recursive_invoke_result_t<F, std::vector<int>>> << "\n";
assert_no_type<recursive_invoke_result<F, std::vector<std::ostream>>>();
std::cout << "double(int), std::vector<std::vector<int>>: " << print_type<recursive_invoke_result_t<F, std::vector<std::vector<int>>>> << "\n";
std::cout << "double(int), std::vector<std::set<int>>: " << print_type<recursive_invoke_result_t<F, std::vector<std::set<int>>>> << "\n";
using G = decltype([](std::vector<int> const &v) -> std::vector<double> { return {}; });
assert_no_type<recursive_invoke_result<G, std::set<std::set<int>>>>();
std::cout << "std::vector<double>(std::vector<int>): " << print_type<recursive_invoke_result_t<G, std::set<std::vector<int>>>> << "\n";
}