如何返回用lambda模板化的对象?

时间:2017-07-31 19:15:42

标签: c++ c++11 templates lambda c++17

请考虑以下代码:

class BaseTask {
  // Implementation here
};

class BaseSubtask {
  BaseTask *_pTask;
public:
  explicit BaseSubtask(BaseTask *pTask) : _pTask(pTask) { }
  virtual void Run() = 0;
  // The rest of implementation here.
};

template<typename taFunc> class LambdaSubtask {
  taFunc _f;
public:
  explicit LambdaSubtask(BaseTask *pTask, taFunc&& f)
  : BaseSubtask(pTask), _f(std::forward<taFunc>(f))
  { }
  LambdaSubtask(const LambdaSubtask&) = delete;
  LambdaSubtask& operator=(const LambdaSubtask&) = delete;
  LambdaSubtask(LambdaSubtask&&) = delete;
  LambdaSubtask& operator=(LambdaSubtask&&) = delete;
  virtual void Run() override final { _f(); }
  // The rest of implementation here
};

因为我不能在没有指定其模板类型参数的情况下声明LambdaSubtask对象,并且我无法指定其模板类型参数,因为它是一个lambda,我尝试实现一个工厂方法:

template<typename taFunc> inline LambdaSubtask<taFunc>
MakeLambdaSubtask(BaseTask *pTask, taFunc&& f) {
  return { pTask, std::forward<taFunc>(f) };
}

不幸的是,这会产生编译错误:

  

LambdaSubtask<lambda_...>的copy-list-initialization不能使用显式构造函数

使用正确的工厂方法,我可以获得LambdaSubtask对象,如下所示:

BaseTask task; // Initialization of the task is skipped in the example
auto&& lst = MakeLambdaSubtask(&task, [/* Capture here*/]() {
  // Implementation here
});

所以基本上我想要一个LambdaSubtask类型的局部变量对象,模板类型是lambda。我想避免额外复制任何东西。当然,我想避免std::function,因为我的基准测试表明它非常慢。

您是否知道如何实现正确的工厂方法或以另一种方式获取LambdaSubtask类型的局部变量对象?

我的编译器是带有工具集v141的MSVC ++ 2017,因此部分支持C ++ 11/14/17。

2 个答案:

答案 0 :(得分:3)

你通过明确声明lambda构造函数来实现自己。删除显式,您的代码应该编译。

请记住,统一列表初始化不适用于显式构造函数。

答案 1 :(得分:2)

从根本上说,你正试图这样做:

struct X {
    explicit X(int ) { }
};

X foo() { return {4}; }

这不起作用,因为X构造函数是explicit并且您正在进行复制列表初始化。解决方法只是明确构造:

X foo() { return X{4}; }
//              ^^^

请注意,在C ++ 17中,这不会导致复制或移动。在C ++ 14之前,这不会产生副本或移动,但移动仍然必须格式良好。

另一个替代方法是从构造函数中删除explicit标记,这是阻止您返回braced-init-list的原因。

作为旁注,请注意:

template<typename taFunc>
LambdaSubtask<taFunc> MakeLambdaSubtask(BaseTask *pTask, taFunc&& f) { ... }

如果你传递了一个左值函数,那么你将保留对它的引用 - 所以这是你需要担心的额外生命。出于这个原因,通常返回LambdaSubtask<std::decay_t<taFunc>>。这可确保子任务在其生命周期内具有有效功能。