仅具有运算符()的结构和普通函数之间的实际区别

Practical difference between a struct with only operator() and a normal function

本文关键字:函数 之间 区别 结构 运算符      更新时间:2023-10-16

我见过这样的代码:

struct foo_functor {
template <typename T, typename U>
constexpr auto operator()(T t, U u) const -> decltype(t | u) {
return t | u;
}
};
constexpr foo_functor foo;

据我所知,它与以下内容相同:

template <typename T, typename U>
constexpr auto foo(T t, U u) -> decltype(t | u) {
return t | u;
}

你为什么要做第一个?有什么区别吗?据我从编译器输出中看到,至少在constexpr的情况下,没有。如果他们不constexpr,在这种情况下会有什么区别吗?

编辑:请注意,与第一个示例非常相似的代码似乎被用来代替正常功能。 6 种不同的结构,所有结构都只有operator()个模板,都像示例的最后一行一样实例化。然后,每个都像正常功能一样使用。

有人在评论中建议函数对象可以具有其他状态。虽然这是真的,但我会更具体一点:您可以创建具有不同状态的函数对象的多个副本。如果函数对象是单例,那么这一点就没有意义了;函数也可以具有全局变量形式的状态。

如果你的函数对象被声明为constexpr,那么它的内部状态都不能是可变的。这使它处于与constexpr函数相同的位置:调用它可以是一个常量表达式,但前提是它不访问任何非常量表达式全局状态。

在C++17之前的一个重要区别是函数可以inline,而对象不能。在 C++14 中,如果在标头中定义了函子foo,则每个翻译单元将有一个副本。如果需要foo在所有翻译单元中具有相同的地址,则需要将其声明为inline函数。但在 C++17 中,函数对象也可以是内联的。

但是,即使您只有函数对象的一个实例,并且它没有状态,并且您使用的是 C++17 或更高版本,它与函数之间至少仍然存在一个重要区别:函数可以通过依赖于参数的查找找到,而函数对象不能。这就是为什么 C++20 Ranges 库中的某些"函数"实际上根本不能是函数,而必须是函数对象的原因。这些被非正式地称为尼布洛伊德。