问题
I would like to create a simple no-op std::function
object with an arbitrary signature. To that end, I've created two functions:
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 {};
}
Each of these works well enough (obviously the first version might create uninitialized data members in the RESULT
object, but in practice I don't think that would be much of a problem). But the second, void
-returning version is necessary because return {};
never returns void
(this would be a compile error), and it can't be written as a template-specialization of the first because the initial signature is variadic.
So I am forced to either pick between these two functions and only implement one, or give them different names. But all I really want is to easily initialize std::function
objects in such a way that, when called, they do nothing rather than throwing an exception. Is this possible?
Note that the default constructor of std::function does not do what I want.
回答1:
I don't like having to specify the signature.
Assuming you have a std::function
implementation where std::function<void()>
can accept a function pointer of type int(*)()
, this is a non-type erased noop
object that can be cast into any 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 {};}
};
if your std::function
does not support that conversion, we add:
template<class...Args>
operator std::function<void(Args...)>() {
return [](auto&&...){};
}
which should handle that case, assuming your std::function
is SFINAE friendly.
live example.
To use, just use noop{}
. If you really need a function returning a noop, do inline noop GetFuncNoop(){ return{}; }
.
A side benefit to this is that if you pass the noop
to a non-type erasing operation, we don't get pointless std::function
overhead for doing nothing.
The reference support is evil because it creates a global object and propogates references to it all over the place. If one std::function<std::string&()>
is called, and the resulting string
modified, the modified string is used everywhere (and without any synchronization between uses). Plus allocating global resources without telling anyone seems rude.
I'd just =delete
the operator T&
case instead, and generate a compile-time error.
回答2:
You are too wedded to braces.
template <typename RESULT, typename... ArgsProto>
std::function<RESULT(ArgsProto...)> GetFuncNoOp()
{
// && avoids unnecessary copying. Thanks @Yakk
return [](ArgsProto&&...) { return RESULT(); };
}
回答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(); };
}
but... @T.C. solution is so much more elegant. Just wanted to show another way that can be applied anywhere you need to specialize just a "part" of something.
来源:https://stackoverflow.com/questions/31274869/create-a-do-nothing-stdfunction-with-any-signature