You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将带Lambda完成函数的std::barrier传入C++命名函数

问题:带自定义完成函数的std::barrier传入命名工作函数时编译失败

我在为密集型信号处理任务编写工作线程,采用C++20的std::jthread(优先于std::thread)。当尝试将带自定义Lambda完成函数的std::barrier传入std::jthread调用的命名工作函数时,出现编译错误;但传入无完成函数的std::barrier,或直接在线程中使用Lambda闭包则可正常编译运行。

问题复现代码

main函数代码

#include <thread>
#include <barrier>

int main() {
    std::barrier barrier(5, []() noexcept {std::cout << "Completion reached\n\n";});

    std::array<std::jthread> threads{}; // 注意:此处std::array未指定大小,本身存在编译问题

    for (auto& thread : threads)
        thread = std::jthread(worker, std::ref(barrier));

    return 0;
}

工作函数代码

#include <thread>
#include <barrier>
#include <iostream>

void worker(std::stop_token stopToken, std::barrier<void(void)>& barrier) noexcept {
    while (!stopToken.stop_requested()) {
        barrier.arrive_and_wait();
    }

    std::cout << "Termination requested for thread " << std::this_thread::get_id() << '\n';
    barrier.arrive_and_drop();
}

编译报错信息

1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.34.31933\include\barrier(76,13): error C2338: static_assert 失败: 'N4861 [thread.barrier.class]/5: is_nothrow_invocable_v<CompletionFunction&> shall be true'
1>C:\Users\Aris\Documents\Development\Perception Neuron\DSP\TestBarrier.cpp(27,16): 消息: 查看对类模板实例化“std::barrier<void (void)>”的引用
1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.34.31933\include\xmemory(1395,1): error C2207: 'std::_Compressed_pair<_Ty1,_Ty2,false>::_Myval1': 类模板的成员不能获取函数类型
1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.34.31933\include\barrier(186,44): 消息: 查看对类模板实例化“std::_Compressed_pair<_Completion_function,std::barrier<_Completion_function>::_Counter_t,false>”的引用
        with
        [
            _Completion_function=void (void)
        ]

可行代码示例(Lambda闭包版本)

#include <thread>
#include <barrier>
#include <iostream>
#include <array>

int main() {
    std::barrier barrier(5, []() noexcept {std::cout << "Completion reached\n\n";});

    std::array<std::jthread, 4> threads{};

    for (auto& thread : threads)
        thread = std::jthread([&barrier] (std::stop_token stopToken) {
            while (!stopToken.stop_requested()) {
                barrier.arrive_and_wait();
            }

            std::cout << "Termination requested for thread " << std::this_thread::get_id() << '\n';
            barrier.arrive_and_drop();
        });

  return 0;
}

解决方案与原理说明

解决方案

方案1:使用auto推导barrier类型(C++20及以上支持)

修改工作函数的参数类型,让编译器自动推导std::barrier的真实类型:

#include <thread>
#include <barrier>
#include <iostream>
#include <array>

void worker(std::stop_token stopToken, auto& barrier) noexcept {
    while (!stopToken.stop_requested()) {
        barrier.arrive_and_wait();
    }

    std::cout << "Termination requested for thread " << std::this_thread::get_id() << '\n';
    barrier.arrive_and_drop();
}

int main() {
    std::barrier barrier(5, []() noexcept {std::cout << "Completion reached\n\n";});

    std::array<std::jthread, 4> threads{}; // 4个工作线程+主线程,匹配barrier计数5

    for (auto& thread : threads)
        thread = std::jthread(worker, std::ref(barrier));

    return 0;
}

方案2:显式指定barrier的类型别名

先通过decltype推导barrier的真实类型,再在工作函数中使用:

#include <thread>
#include <barrier>
#include <iostream>
#include <array>

// 推导barrier的真实类型
using BarrierType = decltype(std::barrier(5, []() noexcept {std::cout << "Completion reached\n\n";}));

void worker(std::stop_token stopToken, BarrierType& barrier) noexcept {
    while (!stopToken.stop_requested()) {
        barrier.arrive_and_wait();
    }

    std::cout << "Termination requested for thread " << std::this_thread::get_id() << '\n';
    barrier.arrive_and_drop();
}

int main() {
    std::barrier barrier(5, []() noexcept {std::cout << "Completion reached\n\n";});

    std::array<std::jthread, 4> threads{};

    for (auto& thread : threads)
        thread = std::jthread(worker, std::ref(barrier));

    return 0;
}

原理说明

  1. std::barrier模板参数的本质:
    std::barrier是模板类,其模板参数是完成函数的具体类型,而非函数签名。Lambda拥有匿名的独特类型,和void(void)这种普通函数类型完全不匹配。工作函数中错误指定std::barrier<void(void)>&,导致编译器实例化了错误的模板版本,触发类型不兼容问题。

  2. 报错原因拆解:

    • 第一个static_assert错误:编译器尝试实例化以void(void)为完成函数类型的std::barrier,但该类型无法满足noexcept可调用的要求,触发标准规定的静态断言检查失败。
    • 第二个类成员类型错误:std::barrier内部用std::_Compressed_pair存储完成函数,而纯函数类型不能作为类成员存储(仅支持函数指针或可调用对象),导致编译失败。
  3. Lambda闭包版本可行的原因:
    闭包通过引用直接捕获了原始的std::barrier对象,编译器可以自动推导其真实类型,无需手动指定,因此不会出现类型不匹配的问题。

内容的提问来源于stack exchange,提问作者user9118496

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 00:20:33