【问题标题】:A recursive_invoke_result_t template structurerecursive_invoke_result_t 模板结构
【发布时间】:2021-04-06 18:03:52
【问题描述】:

我正在尝试使用 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&lt;decltype(f1), int&gt; 似乎工作正常。第二个测试用例recursive_invoke_result_t&lt;decltype(f1), std::vector&lt;int&gt;&gt;,出现编译错误'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。有没有办法解决这个问题?请给我一些提示或例子。

【问题讨论】:

  • 您能否更清楚地解释您要达到的目标?

标签: c++ templates recursion c++20 c++-concepts


【解决方案1】:

这个

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&lt;F, T&gt; 意味着recursive_invoke_result_t_detail&lt;F, T&gt; 仅在std::invocable&lt;F, T&gt; 时有效。时期。任何专业都不能放松这个要求;它是模板本身的属性。对特化的requires 约束只是一个附加的约束:如果std::invocable&lt;F, T&gt;,模板只能用&lt;F, T&gt; 实例化,并且部分特化仅适用于进一步,T = Container&lt;Ts...&gt; 用于某些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 https://stackoverflow.com/a/20170989
    // 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

【讨论】:

  • 您甚至可以使用static_assert(std::is_same_v&lt;double, recursive_invoke_result_t&lt;F, int&gt;); 而不是打印。对于缺少type,它也是可行的,但需要额外的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-17
  • 2011-01-25
  • 1970-01-01
  • 2014-12-22
  • 2017-05-28
  • 1970-01-01
相关资源
最近更新 更多