首页 > 解决方案 > 使用 Y 组合器的空参数包的模板替换失败

问题描述

我正在尝试制作一个函数,该函数将使用boost::asio::posix::stream_descriptor. 我使用async_read_some而不是boost::asio::async_read因为一旦读取大量数据就立即通知我很重要。从管道读取数据后我想做的是

  1. 移动缓冲区的写入器指针
  2. 用新得到的数据做点什么
  3. 安排读取更多数据

我想复制尽可能少的代码,所以我最终得到了一个函数式编程混乱,我很想知道它为什么不起作用。

asio::posix::stream_descriptor inputPipe{context, input.fd};
char buffer[2048]; // simple buffer for demonstration purposes
char* end = buffer + 2048;
char* writer = buffer;

// the interesting part
yCombinator([&](auto&& self_) {
    inputPipe.async_read_some(asio::buffer(writer, end - writer), [&](boost::system::error_code errorCode_, size_t transferedCount_) -> void {
        writer += transferedCount_;
        
        // ... be something, go somewhere, do something, make things change ...
        
        self_(self_); // self_ contains the async_read_some, so it schedules the next read
    });
})();

我自豪地展示yCombinator为:

template<typename Fn>
constexpr auto yCombinator(Fn&& fn_) noexcept {
    return [capture = std::tuple{std::forward<Fn>(fn_)}](auto&&... args_) constexpr noexcept(std::is_nothrow_invocable_v<Fn, decltype(args_)...>)->std::invoke_result_t<Fn, decltype(args_)...> {
        return std::invoke(std::get<0>(capture), std::get<0>(capture), std::forward<decltype(args_)>(args_)...);
    };
}

目前上述代码无法编译。G++ 抱怨说candidate template ignored: substitution failure [with args_:auto = <>]: no type named 'type' in 'std::invoke_result<(lambda at redacted.cpp:80:21)>'. 我知道这与从yCombinator. 我想问题可能args_是一个空的参数包,但我不确定如何处理它。但同时,如果这是问题所在,那为什么这个std::is_nothrow_invocable_v特征看起来一切都好呢?

标签: c++templateslambdafunctional-programmingboost-asio

解决方案


你有几个问题:

首先,返回类型/noexcept 和函数体不匹配:前者Fn中的miss

template<typename Fn>
constexpr auto yCombinator(Fn&& fn_) noexcept {
    return [capture = std::tuple{std::forward<Fn>(fn_)}](auto&&... args_) constexpr
        noexcept(std::is_nothrow_invocable_v<Fn, Fn, decltype(args_)...>)
//                                               ^^
        -> std::invoke_result_t<Fn, Fn, decltype(args_)...>
//                                  ^^
    {
        return std::invoke(std::get<0>(capture),
                           std::get<0>(capture),
                           std::forward<decltype(args_)>(args_)...);
    };
}

然后,要推断未提供的返回类型,我们必须“查看”主体,因此self_在推断之前使用返回类型。

解决方案是明确提供类型:

yCombinator([&](auto&& self_) -> void
//                            ^^^^^^^
{
    // ...
    self_(self_);
})();

推荐阅读