为什么 std::function 实例有默认构造函数?
这可能是一个哲学问题,但我遇到了以下问题:
如果您定义了一个 std::function,并且没有正确初始化它,您的应用程序将崩溃,如下所示:
typedef std::function<void(void)> MyFunctionType;
MyFunctionType myFunction;
myFunction();
如果该函数作为参数传递,像这样:
void DoSomething (MyFunctionType myFunction)
{
myFunction();
}
然后,当然,它也会崩溃。这意味着我被迫添加如下检查代码:
void DoSomething (MyFunctionType myFunction)
{
if (!myFunction) return;
myFunction();
}
要求这些检查让我回想起旧的 C 时代,在那里您还必须显式检查所有指针参数:
void DoSomething (Car *car, Person *person)
{
if (!car) return; // In real applications, this would be an assert of course
if (!person) return; // In real applications, this would be an assert of course
...
}
幸运的是,我们可以在 C++ 中使用引用,这可以防止我编写这些检查(假设调用者没有将 nullptr 的内容传递给函数:
void DoSomething (Car &car, Person &person)
{
// I can assume that car and person are valid
}
那么,为什么 std::function 实例有默认构造函数?如果没有默认构造函数,您就不必添加检查,只需就像 a 的其他正常参数一样 功能。 在那些“罕见”的情况下,您想要传递“可选”std::function,您仍然可以传递指向它的指针(或使用 boost::optional)。
This is probably a philosophical question, but I ran into the following problem:
If you define an std::function, and you don't initialize it correctly, your application will crash, like this:
typedef std::function<void(void)> MyFunctionType;
MyFunctionType myFunction;
myFunction();
If the function is passed as an argument, like this:
void DoSomething (MyFunctionType myFunction)
{
myFunction();
}
Then, of course, it also crashes. This means that I am forced to add checking code like this:
void DoSomething (MyFunctionType myFunction)
{
if (!myFunction) return;
myFunction();
}
Requiring these checks gives me a flash-back to the old C days, where you also had to check all pointer arguments explicitly:
void DoSomething (Car *car, Person *person)
{
if (!car) return; // In real applications, this would be an assert of course
if (!person) return; // In real applications, this would be an assert of course
...
}
Luckily, we can use references in C++, which prevents me from writing these checks (assuming that the caller didn't pass the contents of a nullptr to the function:
void DoSomething (Car &car, Person &person)
{
// I can assume that car and person are valid
}
So, why do std::function instances have a default constructor? Without default constructor you wouldn't have to add checks, just like for other, normal arguments of a function.
And in those 'rare' cases where you want to pass an 'optional' std::function, you can still pass a pointer to it (or use boost::optional).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
它没有“无效”状态。它并不比这更无效:
你拥有的是一个空
函数
,就像aVector
是一个空向量
>。该对象处于一种非常明确的状态:没有数据的状态。现在,让我们考虑一下您的“函数指针”建议:
您必须如何调用它?好吧,有一件事你不能做:
这是不允许的,因为 CallbackFunc 是一个函数,而参数类型是 std::function;*。这两个是不可转换的,所以编译器会抱怨。因此,为了进行调用,您必须执行以下操作:
您刚刚将
new
引入到图片中。您已分配资源;谁将为此负责?回调注册器
?显然,您可能想要使用某种智能指针,因此您会更加混乱界面:这会带来很多 API 烦恼和麻烦,只是为了传递函数。避免这种情况的最简单方法是允许
std::function
为空。就像我们允许 std::vector 为空一样。就像我们允许 std::string 为空一样。就像我们允许 std::shared_ptr 为空一样。等等。简而言之:
std::function
包含一个函数。它是可调用类型的持有者。因此,它有可能不包含可调用类型。It does not have an "invalid" state. It is no more invalid than this:
What you have is an empty
function
, just likeaVector
is an emptyvector
. The object is in a very well-defined state: the state of not having data.Now, let's consider your "pointer to function" suggestion:
How do you have to call that? Well, here's one thing you cannot do:
That's not allowed because
CallbackFunc
is a function, while the parameter type is astd::function<void()>*
. Those two are not convertible, so the compiler will complain. So in order to do the call, you have to do this:You have just introduced
new
into the picture. You have allocated a resource; who is going to be responsible for it?CallbackRegistrar
? Obviously, you might want to use some kind of smart pointer, so you clutter the interface even more with:That's a lot of API annoyance and cruft, just to pass a function around. The simplest way to avoid this is to allow
std::function
to be empty. Just like we allowstd::vector
to be empty. Just like we allowstd::string
to be empty. Just like we allowstd::shared_ptr
to be empty. And so on.To put it simply:
std::function
contains a function. It is a holder for a callable type. Therefore, there is the possibility that it contains no callable type.实际上,您的应用程序不应该崩溃。
该行为被完美指定。
Actually, your application should not crash.
The behavior is perfectly specified.
std::function 最常见的用例之一是注册回调,在满足某些条件时调用。允许未初始化的实例可以仅在需要时注册回调,否则您将被迫始终传递至少某种无操作函数。
One of the most common use cases for
std::function
is to register callbacks, to be called when certain conditions are met. Allowing for uninitialized instances makes it possible to register callbacks only when needed, otherwise you would be forced to always pass at least some sort of no-op function.答案可能是历史性的:
std::function
是作为函数指针的替代品,并且函数指针具有为NULL
的能力。因此,当您想要提供与函数指针的简单兼容性时,您需要提供无效状态。可识别的无效状态并不是真正必要的,因为正如您所提到的,boost::Optional 可以很好地完成这项工作。所以我想说 std::function 的存在只是为了历史。
The answer is probably historical:
std::function
is meant as a replacement for function pointers, and function pointers had the capability to beNULL
. So, when you want to offer easy compatibility to function pointers, you need to offer an invalid state.The identifiable invalid state is not really necessary since, as you mentioned,
boost::optional
does that job just fine. So I'd say thatstd::function
's are just there for the sake of history.在某些情况下,您无法在构造时初始化所有内容(例如,当一个参数取决于对另一个构造的影响,而另一个构造又取决于对第一个构造的影响时......)。
在这种情况下,您必须打破循环,承认稍后纠正可识别的无效状态。
因此,您将第一个元素构造为“null”,构造第二个元素,然后重新分配第一个元素。
实际上,您可以避免检查,如果在使用函数的情况下您在嵌入该函数的对象的构造函数内授予该函数,那么您将始终在有效的重新分配后返回。
There are cases where you cannot initialize everything at construction (for example, when a parameter depends on the effect on another construction that in turn depends on the effect on the first ...).
In this cases, you have necessarily to break the loop, admitting an identifiable invalid state to be corrected later.
So you construct the first as "null", construct the second element, and reassign the first.
You can, actually, avoid checks, if -where a function is used- you grant that inside the constructor of the object that embeds it, you will always return after a valid reassignment.
就像可以将空状态添加到没有空状态的仿函数类型中一样,您可以使用不允许空状态的类来包装仿函数。前者需要添加状态,后者不需要新状态(仅是一个限制)。因此,虽然我不知道 std::function 设计的基本原理,但它支持最精简的设计。意思是用法,无论你想要什么。
干杯&呵呵,,
In the same way that you can add a nullstate to a functor type that doesn't have one, you can wrap a functor with a class that does not admit a nullstate. The former requires adding state, the latter does not require new state (only a restriction). Thus, while i don't know the rationale of the
std::function
design, it supports the most lean & mean usage, no matter what you want.Cheers & hth.,
您只需使用 std::function 进行回调,您可以使用一个简单的模板辅助函数,如果它不为空,则将其参数转发给处理程序:
并按以下方式使用它:
You just use std::function for callbacks, you can use a simple template helper function that forwards its arguments to the handler if it is not empty:
And use it in the following way: