我见过这样的代码:
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;
Run Code Online (Sandbox Code Playgroud)
据我所知,它与以下内容相同:
template <typename T, typename U>
constexpr auto foo(T t, U u) -> decltype(t | u) {
return t | u;
}
Run Code Online (Sandbox Code Playgroud)
为什么要做第一个?有什么区别吗?据我从编译器输出中看到,至少在constexpr
,没有。如果不是constexpr
,那会不会有什么不同呢?
编辑:请注意,与第一个示例非常相似的代码似乎被用来代替普通函数。6 种不同的结构,都只有operator()
模板,都像示例的最后一行一样被实例化。然后每个都像正常功能一样使用。
有人在评论中建议函数对象可以有额外的状态。虽然这是真的,但我会更具体一点:您可以创建具有不同状态的函数对象的多个副本。如果函数对象是单例,那么这点就没有实际意义;函数也可以具有全局变量形式的状态。
如果你的函数对象被声明constexpr
,那么它的内部状态都不能是可变的。这将它置于与constexpr
函数相同的位置:调用它可以是一个常量表达式,但前提是它不访问任何非常量表达式全局状态。
C++17 之前的一个重要区别是函数可以是inline
,而对象不能是。在 C++14 中,如果您foo
在头文件中定义函子,那么每个翻译单元将有一个它的副本。如果需要foo
在所有翻译单元中使用相同的地址,则需要将其声明为inline
函数。但在 C++17 中,函数对象也可以内联。
但是即使你只有一个函数对象的实例,并且它没有状态,而且你使用的是 C++17 或更高版本,它和函数之间至少还有一个重要的区别:函数可以通过参数相关查找,而函数对象不能。这就是为什么 C++20 Ranges 库中的一些“函数”实际上根本不能是函数,而必须是函数对象的原因。这些被非正式地称为niebloids。