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

C++20 OperatorLike concept添加双约束时编译失败原因

C++ OperatorLike概念添加get_int约束后编译失败问题

问题复现代码

#include <concepts>
#include <functional>
#include <iostream>

template<typename T>
concept OperatorLike = requires(T t, const std::string s) {
    { t.get_string(s) } -> std::same_as<const std::string>;
    { t.get_int(s) } -> std::same_as<const int>;
};

template<typename T, typename O>
concept Gettable = requires(T t, O op) { 
    t.apply_get(0, op);  
    t.apply_post(0, op); 
};

template<std::semiregular F>
class RestApiImpl {
    F m_get_method;
public:
    RestApiImpl(F get = F{}) : m_get_method{std::move(get)} {}
    void register_get(F functor) {
       m_get_method = std::move(functor);
    }
    template<OperatorLike IF>
        requires std::invocable<F, const int, IF>
    void apply_get(const int req, IF interface){ 
        m_get_method(req, std::move(interface));  
    }

    template<OperatorLike IF>
        requires std::invocable<F, const int, IF>
    void apply_post(const int req, IF interface){ 
        // 注意:此处为复制粘贴错误,应调用post对应方法,不影响本次编译问题
        m_get_method(req, std::move(interface));  
    }
};

class ASpecificJSONLibrary{
public:
    std::string operator[](std::string key){
        return key + "_withLambda";
    }
};

class Server{
public:
   ASpecificJSONLibrary libObj;
   struct impl;
   void run(Gettable<impl> auto& api){
      api.apply_get(0, impl(*this));
   }

   struct impl {
   public:
        Server& m_server;
        impl(Server& server ) : m_server(server){}
        const std::string get_string(const std::string key) {
            return (m_server.libObj)[key];
        };
        const int get_int(std::string const key) {
            return 1;
        }
   };
};

int main(){
    auto get = [](int, OperatorLike auto intf){ 
        std::string dummy = "dummy";
        std::cout << intf.get_string(dummy);
    };
    RestApiImpl api(get);
    
    Server server;
    server.run(api);
    
    return 0;
}

现象描述

  • 从OperatorLike概念中删除{ t.get_int(s) } -> std::same_as<const int>;约束时,代码可正常编译运行
  • 保留该get_int约束时,编译报错提示Server::run方法无匹配调用,表层原因是Gettable<Server::impl>概念未满足,根因出在OperatorLike的定义
  • 若移除OperatorLike概念中返回值匹配的所有const限定符,即使保留get_int相关约束,代码也可正常编译通过

根本原因

问题由两个C++语言规则和std::same_as的特性共同导致:

  1. std::same_as是严格类型匹配,不会执行任何隐式类型转换或类型退化
  2. C++表达式类型规则明确:非类、非数组类型的prvalue(比如int类型的函数返回值)的顶层cv限定符会被直接丢弃;类类型prvalue的顶层cv限定符会被保留。

对应到代码逻辑:

  • get_string返回const std::string,std::string是类类型,调用表达式t.get_string(s)的类型就是const std::string,所以std::same_as<const std::string>检查可以通过
  • get_int返回const int,int是标量类型(非类非数组),调用表达式t.get_int(s)的顶层const会被语言规则自动剥离,实际类型是int,和const int不匹配,直接导致OperatorLike<Server::impl>约束检查失败

OperatorLike检查失败后,RestApiImpl::apply_get的模板重载无法被匹配,进一步导致Gettable概念检查失败,最终编译器抛出run方法无匹配的错误。
移除所有const限定符后代码可以编译,是因为此时两个方法的返回值类型分别是std::string和int,和表达式实际类型完全匹配,所有概念检查都能通过。

另外补充:按值返回的变量加顶层const本身就是无意义的写法,既不会阻止调用方修改返回值,也不会带来任何性能收益,属于应当避免的不良实践。

修复方案

直接修改OperatorLike概念的返回值约束,去掉无意义的顶层const匹配要求即可,同时建议删除get_string、get_int返回值前多余的const:

template<typename T>
concept OperatorLike = requires(T t, const std::string s) {
    { t.get_string(s) } -> std::same_as<std::string>;
    { t.get_int(s) } -> std::same_as<int>;
};

修改后代码即可正常编译运行。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:06:23