一个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 templateunable 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";
}

Godbolt