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的特性共同导致:
std::same_as是严格类型匹配,不会执行任何隐式类型转换或类型退化- 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
相关产品推荐
相关产品推荐

