与 std::semi_regular 一起使用时,带有捕获的 lambda 无法编译

ATK*_*ATK 3 c++ lambda c++20

一旦 lambda 捕获任何变量,以下示例就无法编译。如果删除捕获,编译器将成功编译下面的代码。

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

template<typename T>
concept OperatorLike = requires(T t, std::string s) {
    { t[s] } -> std::same_as<std::string>;
};

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

template<std::semiregular F>
class RestApiImpl {
    F m_post_method;
    public:
    RestApiImpl(F get = F{}) : m_post_method{std::move(get)} {}

    template<OperatorLike IF>
    requires std::invocable<F, const int, IF>
    void apply_post(const int req, IF interface){ 
        m_post_method(req, std::move(interface));  
    }
};


int main(){

    int ii;
    auto get = [&ii](int,  OperatorLike auto intf){ 
            std::string dummy = "dummy";
        std::cout << intf[dummy];
    };
    RestApiImpl api(get);
    return 0;
};
Run Code Online (Sandbox Code Playgroud)

如果 lambda 函数 get() 有空括号 [] ,意味着没有捕获,它将编译。

该类的需求RestApiImpl基于以下应用:

class Server{
public:


   struct impl;

   void run(Gettable<impl> auto& api){
      api.apply_post(0, impl(*this));
   }

   struct impl {
        public:
        Server& m_server;
        impl(Server& server ) : m_server(server){}
        std::string operator[](std::string key) {
            return "dummy_for_now";
        }
   };
};

int main(){
    auto get = [&ii](int,  OperatorLike auto intf){ 
            std::string dummy = "dummy";
        std::cout << intf[dummy];
    };
    RestApiImpl api(get);
    
    Server server;
    server.run(api);
    
    return 0;
};
Run Code Online (Sandbox Code Playgroud)

错误信息:

:35:24: 错误:类模板参数推导失败:35 | RestApiImpl api(获取); | ^ :35:24: 错误: 没有匹配的函数来调用 'RestApiImpl(main()::&)' :18:5: 注意:候选: 'template RestApiImpl(F)-> RestApiImpl' 18 | RestApiImpl(F get = F{}) : m_post_method{std::move(get)} {} | ^~~~~~~~~~~ :18:5: 注意:模板参数推导/替换失败: : 替换 'template RestApiImpl(F)-> RestApiImpl [with F = main()::]': :35:24:从这里需要:18:5:错误:“模板需要半规则类 RestApiImpl”的模板约束失败:18:5:注意:不满足约束

为了克服这个问题我有什么选择?

use*_*522 7

std::semiregular需要std::copyablestd::default_initializable

std::copyable意味着它要求类型是可复制构造和可复制分配的。

std::default_initializable意味着它要求类型T具有格式良好的初始化,形式为T t;,T()T{}

具有捕获的 lambda 具有删除的复制赋值运算符、无移动赋值运算符和默认构造函数,而对于无捕获的 lambda (C++20 起),所有这些都是默认的。因此,带有捕获的 lambda 不是std::copyable(或std::movable就此而言)并且不是std::default_initializable,因此尤其也不是std::semiregular,而无捕获的 lambda 是所有这些。

RestApiImpl要求F get构造函数中传递的参数为std::semiregular


为了解决这个问题,请重新考虑是否RestApiImpl真的需要该类型的所有这些属性。

我可以看到它需要 中的移动构造性m_post_method{std::move(get)},但不清楚它在哪里使用例如您所展示的复制可分配性。

= F{}使用默认构造性,但如果您不默认构造RestApiImpl具有该类型的实例,则不会使用或实例化该默认值。

然后使用不像 那样具有限制性的概念std::semiregular

如果这不可能,那么您就不能直接使用这样的 lambda。相反,您可以使用旧式函子类型,例如重载的类operator(),并将 a 存储std::reference_wrapper<int>ii对象中。这确保了可分配性,因为作为成员的简单引用将使该类不可分配。