用任何签名创建一个"do-nothing"`std :: function`?

Kyl*_*and 5 c++ lambda variadic-templates c++11 std-function

我想创建一个std::function带有任意签名的简单无操作对象.为此,我创建了两个函数:

template <typename RESULT, typename... ArgsProto>
  std::function<RESULT(ArgsProto...)> GetFuncNoOp()
  {
    // The "default-initialize-and-return" lambda
    return [](ArgsProto...)->RESULT { return {}; };
  }

template <typename... ArgsProto>
  std::function<void(ArgsProto...)> GetFuncNoOp()
  {
    // The "do-nothing" lambda
    return [](ArgsProto...)->void {};
  }
Run Code Online (Sandbox Code Playgroud)

这些中的每一个都运行得很好(显然第一个版本可能在RESULT对象中创建未初始化的数据成员,但实际上我认为这不会是一个大问题).但是第二个 - void返回版本是必要的,因为return {}; 永远不会返回void(这将是编译错误),并且它不能被写为第一个的模板特化,因为初始签名是可变参数.

所以我不得不在这两个函数之间进行选择,只实现一个,或给它们不同的名称.但我真正想要的是以std::function这样的方式轻松初始化对象:当被调用时,它们什么也不做,而不是抛出异常.这可能吗?

请注意,默认构造函数std::function不能做我想要的.

T.C*_*.C. 8

你太拘泥于牙套了.

template <typename RESULT, typename... ArgsProto>
std::function<RESULT(ArgsProto...)> GetFuncNoOp()
{
  // && avoids unnecessary copying. Thanks @Yakk
  return [](ArgsProto&&...) { return RESULT(); }; 
}
Run Code Online (Sandbox Code Playgroud)

  • 哦,我没有意识到`void()`可以这样"初始化".那里到底发生了什么?它和`return;`有什么不同吗? (2认同)

Yak*_*ont 5

我不喜欢必须指定签名。

假设你有一个std::function实施方案,其中std::function<void()>可以接受类型的函数指针int(*)(),这是一个非类型擦除noop可铸造成任何对象std::function

struct noop {
  struct anything {
    template<class T>
    operator T(){ return {}; }
    // optional reference support.  Somewhat evil.
    template<class T>
    operator T&()const{ static T t{}; return t; }
  };
  template<class...Args>
  anything operator()(Args&&...)const{return {};}
};
Run Code Online (Sandbox Code Playgroud)

如果您std::function不支持该转换,我们添加:

  template<class...Args>
  operator std::function<void(Args...)>() {
    return [](auto&&...){};
  }
Run Code Online (Sandbox Code Playgroud)

假设您对std::functionSFINAE 友好,应该处理这种情况。

活生生的例子

要使用,只需使用noop{}. 如果您真的需要一个返回 noop 的函数,请执行inline noop GetFuncNoop(){ return{}; }.

这样做的一个附带好处是,如果您将 传递noop给非类型擦除操作,我们不会std::function因为什么都不做而获得无意义的开销。

引用支持是邪恶的,因为它创建了一个全局对象并在所有地方传播对它的引用。如果std::function<std::string&()>调用了一个,并string修改了结果,则修改后的字符串将在任何地方使用(并且在使用之间没有任何同步)。加上在不告诉任何人的情况下分配全球资源似乎很粗鲁。

我刚刚=deleteoperator T&情况相反,并生成一个编译时错误。