考虑以下代码:

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对象,如下所示:
BaseTask task; // Initialization of the task is skipped in the example
auto&& lst = MakeLambdaSubtask(&task, [/* Capture here*/]() {
  // Implementation here
});

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

您知道如何实现适当的工厂方法或以其他方式获取LambdaSubtask类型的局部变量对象吗?

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

最佳答案

从根本上讲,您正在尝试执行以下操作:

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

X foo() { return {4}; }

那是行不通的,因为X构造函数是explicit,并且您正在执行复制列表初始化。解决方法是显式构造:
X foo() { return X{4}; }
//              ^^^

请注意,在C++ 17中,这不会引起复制或移动。在C++ 14之前,这既不会引起复制也不会产生任何举动,但是举动仍然必须格式正确。

另一种选择是从构造函数中删除explicit标记,这是阻止您返回支撑初始列表的原因。

作为旁注,请注意以下事项:
template<typename taFunc>
LambdaSubtask<taFunc> MakeLambdaSubtask(BaseTask *pTask, taFunc&& f) { ... }

如果您传入的是左值函数,则将保留对其的引用-因此,这是您必须担心的额外寿命。因此,通常返回LambdaSubtask<std::decay_t<taFunc>>。这样可以确保子任务在其生命周期内具有有效的功能。

09-28 02:01