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

升级至GCC12.3后C++20 Concept代码编译失败问题求助

问题背景

示例代码

#include <stop_token>
#include <type_traits>

template <typename TOwner>
concept HasGetStopToken = requires(TOwner& o) {
    { o.GetStopToken() } -> std::same_as<std::stop_token>;
};

template <typename TOwner>
class RequestLooper
{
public:

    explicit RequestLooper(TOwner& _owner)
        requires HasGetStopToken<TOwner>
        : m_owner{ _owner }
    {
    }

private:
    TOwner& m_owner;
};

class CoreApplication {

public:
    std::stop_token GetStopToken() { return m_stopSource.get_token(); }

    using Looper = RequestLooper<CoreApplication>;

    CoreApplication() : m_looper{*this}
    {
    }

private:
    Looper m_looper;
    std::stop_source m_stopSource;
};

编译错误信息

In instantiation of 'RequestLooper<TOwner>::RequestLooper(TOwner&) requires  HasGetStopToken<TOwner> [with TOwner = CoreApplication]':
<source>:31:25:   required from here
<source>:5:9:   required for the satisfaction of 'HasGetStopToken<TOwner>' [with TOwner = CoreApplication]
<source>:5:27:   in requirements with 'TOwner& o' [with TOwner = CoreApplication]
<source>:5:27: error: satisfaction value of atomic constraint 'requires(TOwner& o) {{o->GetStopToken()} -> decltype(auto) [requires std::same_as<<placeholder>, std::stop_token>];} [with TOwner = CoreApplication]' changed from 'false' to 'true'
    5 | concept HasGetStopToken = requires(TOwner& o) {
      |                           ^~~~~~~~~~~~~~~~~~~~~
    6 |     { o.GetStopToken() } -> std::same_as<std::stop_token>;
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    7 | };
      | ~                           
<source>:10:7: note: satisfaction value first evaluated to 'false' from here
   10 | class RequestLooper
      |       ^~~~~~~~~~~~~
<source>: In constructor 'CoreApplication::CoreApplication()':
<source>:31:25: error: no matching function for call to 'RequestLooper<CoreApplication>::RequestLooper(<brace-enclosed initializer list>)'
   31 |     CoreApplication() : m_looper{*this}
      |                         ^~~~~~~~~~~~~~~
<source>:14:14: note: candidate: 'RequestLooper<TOwner>::RequestLooper(TOwner&) requires  HasGetStopToken<TOwner> [with TOwner = CoreApplication]'
   14 |     explicit RequestLooper(TOwner& _owner)
      |              ^~~~~~~~~~~~~
<source>:14:14: note: constraints not satisfied
<source>:10:7: note: candidate: 'constexpr RequestLooper<CoreApplication>::RequestLooper(const RequestLooper<CoreApplication>&)'
   10 | class RequestLooper
      |       ^~~~~~~~~~~~~
<source>:10:7: note:   no known conversion for argument 1 from 'CoreApplication' to 'const RequestLooper<CoreApplication>&'
<source>:10:7: note: candidate: 'constexpr RequestLooper<CoreApplication>::RequestLooper(RequestLooper<CoreApplication>&&)'
<source>:10:7: note:   no known conversion for argument 1 from 'CoreApplication' to 'RequestLooper<CoreApplication>&&'
Compiler returned: 1

核心问题

  • 这是已知的编译器bug吗?
  • 在Ubuntu22.04上是否有无需更新glibc即可升级至GCC13的方法?
  • 是否存在无需移除Concept的代码兼容方案?

解答

1. 是否为已知编译器bug

这确实是GCC 12.3和12.4版本的回归bug。该问题属于C++概念约束在类成员初始化阶段的评估逻辑错误,具体表现为对HasGetStopToken概念的原子约束真值出现前后矛盾的判定。此bug仅存在于GCC 12.3/12.4,在GCC 12.1、12.2以及13.x及以上版本均已修复。

2. Ubuntu22.04无需更新glibc安装GCC13的方法

可以采用以下两种可行方案:

  • 使用Snap包安装:Snap包自带独立依赖,不会依赖系统glibc版本。执行命令sudo snap install gcc --classic --channel=13/stable即可安装GCC13,安装完成后可通过gcc-13或g++-13调用。
  • 手动编译GCC13:从GCC官网下载源码包,编译时指定--with-glibc-version=2.35(Ubuntu22.04默认glibc版本为2.35),并将编译产物安装到非系统目录(如/opt/gcc13),避免覆盖系统原有编译器。编译过程需依赖build-essential、flex、bison等工具,可提前通过apt安装。

3. 无需移除Concept的代码兼容方案

有两种可靠的代码调整方式,均可绕过GCC12.3/12.4的bug:

  • 调整类成员声明顺序:将CoreApplication中std::stop_source m_stopSource的声明移至Looper m_looper之前。修改后代码如下:
class CoreApplication {
    // ... 其他代码保持不变
private:
    std::stop_source m_stopSource; // 先声明该成员,确保构造m_looper时已存在
    Looper m_looper;
};

此方案解决了编译器在评估概念时,因m_stopSource未提前声明导致的约束判定异常。

  • 修改Concept的写法:拆分原概念中的返回值类型检查,将其拆分为独立的requires子句,避免编译器的原子约束判定bug。修改后的Concept如下:
template <typename TOwner>
concept HasGetStopToken = requires(TOwner& o) {
    o.GetStopToken(); // 先确保成员函数可调用
    requires std::same_as<decltype(o.GetStopToken()), std::stop_token>; // 单独检查返回值类型
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 02:52:01