【问题标题】:Passing a Lambda Expression to std::function in C++在 C++ 中将 Lambda 表达式传递给 std::function
【发布时间】:2022-01-19 01:39:45
【问题描述】:

我目前正在努力使用 lambda 表达式。 我创建了一个 ConcurrentDictionary ,它用互斥锁包装了 std::map 。 现在我想导出所有符合指定条件的元素。

template<typename MutexTypeT, typename Key_Type_T, typename Mapped_Type_T>
class CConcurrentDictionary
{
    public:
        ///Constructor
        CConcurrentDictionary();

        class CSingleElement
        {
        public:

          ///the key
          Key_Type_T key = { };

          ///THe Mapped Type
          Mapped_Type_T mapped_type = { };

        };

     template<typename ...Args_T>
        std::vector<CSingleElement> exportSelectedData(
            const uint32_t u32MutexTimeout,
            std::function<bool(const CSingleElement, Args_T &&...)> compareFn,
            Args_T&&...CompareArgs...) const;

}

template<typename MutexTypeT, typename Key_Type_T, typename Mapped_Type_T>
   template<typename ...Args_T>
   auto CConcurrentDictionary<MutexTypeT, Key_Type_T, Mapped_Type_T>::exportSelectedData(
          const uint32_t u32MutexTimeout,
          std::function<bool(const CSingleElement, Args_T &&...)> compareFn,
          Args_T&&...CompareArgs...) const ->
      std::vector<CSingleElement>
      {
        //lock mutex...
        std::vector<CSingleElement> vecRes;
        for (const auto &single_element : dictionary)
        {
          if(compareFn(single_element, CompareArgs...))
          {
            //this element mathes the preconditions
            vecRes.push_back(single_element);
          }
        }

        return vecRes;
      }

但是当我尝试像这样使用这个类时

 class CTraceCmdDuration
  {

  public:


    ///Cmd Datatype
    using CmdType_T = uint32_t;

    class CCmdSummary
    {
    public:
      /**
       * SIngle Long Running Cmd Summary
       * @param CmdIdArg Cmd ID
       * @param u32MaxCmdDurationArg Max CmdDuration
       */
      CCmdSummary(const CmdType_T CmdIdArg, const uint32_t u32MaxCmdDurationArg);
      ///Id of this cmd
      const CmdType_T CmdId;

      ///Duration of this cmd
      const uint32_t u32MaxCmdDuration;
    };

/**
     * Exports all Cmds to a vector with took longer than u32MaxCmdDuration
     * @param u32MaxCmdDuration Maximal Cmd Processing duration time. Cmds that took longer than this will be exported
     * @param u32MutexTimeout Mutex Timeout
     * @return List with all long running cmds
     */
    std::vector<CCmdSummary> ExportLongRunningCmds(const uint32_t u32MaxCmdDuration, const uint32_t u32MutexTimeout) const;

}

  auto CTraceCmdDuration::ExportLongRunningCmds(const uint32_t u32MaxCmdDuration, const uint32_t u32MutexTimeout) const ->
      std::vector<CCmdSummary>
  {
    auto lambda = [u32MaxCmdDuration](const CombinedDictElement& singleElement)
        {
          const bool bRes = (u32MaxCmdDuration < singleElement.mapped_type.u32MaxProcessingDuration);
          return bRes;
        };
    auto vecRes = dict.exportSelectedData(u32MutexTimeout, lambda, u32MaxCmdDuration);
    return vecRes;
  }

不幸的是,这不会产生对函数错误的匹配调用

error: no matching function for call to 'NConcurrent_Container::CConcurrentDictionary<NMutex::CEmbos_Mutex, long unsigned int, NDebug::CTraceCmdDuration::CProcessingInfo>::exportSelectedData(const uint32_t&, NDebug::CTraceCmdDuration::ExportLongRunningCmds(uint32_t, uint32_t) const::__lambda0&, const uint32_t&) const'
     auto vecRes = dict.exportSelectedData(u32MutexTimeout, lambda, u32MaxCmdDuration);

我的 lambda 表达式有什么问题? 想法是将最大允许持续时间作为捕获传递,并将 std::map 中的每个存储元素作为参数传递。

或者你有更好的主意吗? 你能帮帮我吗?

编辑: 谢谢你的回答,如果我传递一个静态函数,这很好用 但是我如何将 lambda 作为模板参数传递呢?

static bool CompareDuaration(const CSingleElement&singleElement, const uint32_t u32MaxDuration);

  auto CTraceCmdDuration::ExportLongRunningCmds(const uint32_t u32MaxCmdDuration, const uint32_t u32MutexTimeout) const ->
      std::vector<CombinedDictElement>
  {
    auto vecRes = dict.exportSelectedData(u32MutexTimeout, lambda, u32MaxCmdDuration);
    return vecRes;
  }

这可行,但是

auto CTraceCmdDuration::ExportLongRunningCmds(const uint32_t u32MaxCmdDuration, const uint32_t u32MutexTimeout) const ->
      std::vector<CombinedDictElement>
  {

    auto lambda = [u32MaxCmdDuration](const CombinedDictElement& singleElement)
        {
          const bool bRes = (u32MaxCmdDuration < singleElement.mapped_type.u32MaxProcessingDuration);
          return bRes;
        };

    auto vecRes = dict.exportSelectedData(u32MutexTimeout, lambda, u32MaxCmdDuration);
    return vecRes;
  }

给我一​​个编译错误

error: no match for call to '(CTraceCmdDuration::ExportLongRunningCmds(uint32_t, uint32_t) const::__lambda0) (CConcurrentDictionary<NMutex::CEmbos_Mutex, long unsigned int, CTraceCmdDuration::CProcessingInfo>::CSingleElement&, const long unsigned int&)'
       if(compareFn(singleElement, compareArgs...))

似乎将 lambdas 传递给模板并不能很好地工作。我错过了什么?

【问题讨论】:

  • 什么是CombinedDictElement?

标签: c++ lambda


【解决方案1】:

问题

你的问题可以归结为:

#include <functional>

template <typename ...Args>
void foo(std::function<bool(Args...)>) { }

int main()
{
    foo([](int, int) { return true; });
}

编译失败。原因是std::function 的模板参数的类型推导失败。

您希望将某种std::function 作为参数传递。由于没有传入std::function 对象(无论是什么确切的实例化),编译器会尝试构造一个std::function 并通过调用std::function 的构造函数来推断模板参数类型。问题从这里开始。在这种情况下,匹配的构造函数是:

template <typename F>
function(F f);

您会注意到构造函数本身 也是模板化的。编译器可以成功推导出F为lambda,但由于F是构造函数的模板参数,所以无法推导出类本身的模板参数std::function。

此外,在该构造函数上引用cppreference:

[...] 此构造函数不参与重载决议 除非 f 对于参数类型 Args... 和返回类型 R 是可调用的。 [...]

这意味着这个构造函数的存在是基于是否可以用类模板参数Args...调用F,但是由于这些没有显式定义并且无法推导,所以这个构造函数无论如何都不可用.

解决办法

由于您只在exportSelectedData 中使用std::function,因此只需将其全部设为模板参数(放弃std::function 部分):

template<typename Func, typename ...Args_T>
std::vector<CSingleElement> exportSelectedData(uint32_t u32MutexTimeout, Func compareFn, Args_T const&...) const;

您还应该将Args_T&amp;&amp; 更改为Args_T const&amp;,因为您不只是转发这些参数,而是在循环中重复使用它们。

编辑

关于你在编辑中的后续问题:想想你在做什么。

首先你声明一个 lambda:

auto lambda = [u32MaxCmdDuration](const CombinedDictElement& singleElement) { /* ... */ };

现在想想那个 lambda 的签名。你返回一个布尔值,所以返回类型是bool(到目前为止一切都很好)。您在 capture-clause 中采用 u32MaxCmdDuration 并采用一个参数 singleElement。让我们删除所有 extra 限定符并查看签名:

bool(CombinedDictElement) // take a CombinedDictElement and return a bool

接下来我们看看exportSelectedData的调用:

exportSelectedData(u32MutexTimeout, lambda, u32MaxCmdDuration);

您传入u32MutexTimeout 和lambda,这很好,lambda 被compareFn 捕获。第三个参数是u32MaxCmdDuration,它由模板中的...compareArgs 捕获。现在让我们看看在 exportSelectedData 中实际调用 lambda 的位置:

if (compareFn(singleElement, compareArgs...)) // ...

您希望compareFn 在这里有什么签名?如果我们扩展 ...compareArgs 包(再次,为了简单起见,删除 额外的 限定符)签名看起来像这样:

bool(CombinedDictElement, unsigned int) // take a CombinedDictElement and an unsigned int and return a bool

这里又是 lambda 签名:

bool(CombinedDictElement)

你发现问题了吗? lambda 捕获 u32MaxCmdDuration 作为状态捕获,而 exportSelectedData 期望它作为参数(因为您将它作为附加参数传递给 exportSelectedData)。显然,签名彼此不同,因此我们必须更改其中一个以匹配另一个。在您的情况下,这也很容易

  • 更改您的 lambda 以将 u32MaxCmdDuration 作为参数:

    auto lambda = [](const CombinedDictElement& singleElement, unsigned int u32MaxCmdDuration)
    

    或

  • 删除u32MaxCmdDuration 作为exportSelectedData 调用的附加参数:

     exportSelectedData(u32MutexTimeout, lambda);
    

【讨论】:

  • 嘿,谢谢您的回复 :) 这对于正常功能来说效果很好。但是传递一个 lambda 仍然会导致一些错误。你能帮帮我吗?我相应地编辑了我的问题:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-31
  • 1970-01-01
相关资源
最近更新 更多