我想创建一个带有任意签名的简单无操作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 {};
}
其中每一个都运行良好(显然第一个版本可能会在RESULT
对象中创建未初始化的数据成员,但实际上我并不认为这会是一个很大的问题)。但第二个void
- 返回版本是必要的,因为return {};
从不返回void
(这将是一个编译错误),它不能被写为第一个模板特化,因为初始签名是可变参数。
所以我不得不在这两个函数之间进行选择并只实现一个,或者给它们不同的名称。但我真正想要的只是以这样的方式轻松初始化std::function
个对象,这样当被调用时,它们什么也不做,而不是抛出异常。这可能吗?
请注意the default constructor of std::function
does not do what I want。
答案 0 :(得分:8)
你太拘泥于牙箍了。
template <typename RESULT, typename... ArgsProto>
std::function<RESULT(ArgsProto...)> GetFuncNoOp()
{
// && avoids unnecessary copying. Thanks @Yakk
return [](ArgsProto&&...) { return RESULT(); };
}
答案 1 :(得分:3)
template <class T> struct dummy {
static auto get() -> T { return {}; }
};
template <> struct dummy<void> {
static auto get() -> void {}
};
template <typename RESULT, typename... ArgsProto>
std::function<RESULT(ArgsProto...)> GetFuncNoOp()
{
return [](ArgsProto...)->RESULT { return dummy<RESULT>::get(); };
}
但是...... @ T.C.解决方案更加优雅。只是想展示另一种方法,可以应用于你需要专门化的东西的“部分”。
答案 2 :(得分:2)
我不想指定签名。
假设你有一个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 {};}
};
如果您的std::function
不支持该转化,我们会添加:
template<class...Args>
operator std::function<void(Args...)>() {
return [](auto&&...){};
}
应该处理这种情况,假设您的std::function
是SFINAE友好的。
the comments of the php documentation
要使用,只需使用noop{}
即可。如果你真的需要一个返回noop的函数,请执行inline noop GetFuncNoop(){ return{}; }
。
这样做的另一个好处是,如果您将noop
传递给非类型擦除操作,我们就不会因为什么都不做而获得无意义的std::function
开销。
引用支持是邪恶的,因为它创建了一个全局对象,并在整个地方传播对它的引用。如果调用了一个std::function<std::string&()>
,并且修改了结果string
,则修改后的字符串将在任何地方使用(并且在使用之间没有任何同步)。加上分配全球资源而不告诉任何人似乎很粗鲁。
我只是=delete
operator T&
的情况,而是生成编译时错误。