使用 Y 组合器的空参数包的模板替换失败

Template substitution failure with empty parameter pack using a Y-combinator

我正在尝试创建一个函数,该函数将使用 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 编辑 return 的 lambda 的尾部 return 类型有关。我想问题可能是 args_ 是一个空参数包,但我不确定如何处理它。但与此同时,如果这是问题所在,那么为什么 std::is_nothrow_invocable_v 特征看起来一切正常?

您有几个问题:

首先,returntype/noexcept和函数体不匹配: 错过前者Fn

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_)...);
    };
}

然后,要推断未提供的 return 类型,我们必须 "look" in the body,因此使用 self_ return 在我们推断之前输入。

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

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