为什么 std::function 不能相等比较?
这个问题也适用于 boost::function
和 std::tr1::function
。
std::function
不可进行等式比较:
#include <functional>
void foo() { }
int main() {
std::function<void()> f(foo), g(foo);
bool are_equal(f == g); // Error: f and g are not equality comparable
}
在 C++11 中,operator==
和 operator!=
重载根本不存在。在早期的 C++11 草案中,重载被声明为已删除,并带有注释 (N3092 §20.8.14.2):
// deleted overloads close possible hole in the type system
它没有说明“类型系统中可能的漏洞”是什么。在 TR1 和 Boost 中,声明了重载但未定义。 TR1 规范注释 (N1836 §3.7.2.6):
这些成员函数应保持未定义。
[注意:类似布尔的转换打开了一个漏洞,可以通过
==
或!=
比较两个函数实例。这些未定义的void
运算符封闭了漏洞并确保编译时错误。 —尾注]
我对“漏洞”的理解是,如果我们有一个 bool
转换函数,则该转换可以用于相等比较(以及其他情况下):
struct S {
operator bool() { return false; }
};
int main() {
S a, b;
bool are_equal(a == b); // Uses operator bool on a and b! Oh no!
}
我的印象是,C++03 中的 safe-bool 习惯用法和 C++11 中显式转换函数的使用是为了避免这个“漏洞”。 Boost 和 TR1 都在 function
中使用 safe-bool 习惯用法,并且 C++11 使 bool
转换函数显式化。
作为同时具有这两种功能的类的示例,std::shared_ptr
都具有显式 bool
转换函数并且可以进行相等比较。
为什么 std::function
不能进行相等比较?什么是“类型系统中可能的漏洞?”它与 std::shared_ptr 有何不同?
This question also applies to boost::function
and std::tr1::function
.
std::function
is not equality comparable:
#include <functional>
void foo() { }
int main() {
std::function<void()> f(foo), g(foo);
bool are_equal(f == g); // Error: f and g are not equality comparable
}
In C++11, the operator==
and operator!=
overloads just don't exist. In an early C++11 draft, the overloads were declared as deleted with the comment (N3092 §20.8.14.2):
// deleted overloads close possible hole in the type system
It does not say what the "possible hole in the type system" is. In TR1 and Boost, the overloads are declared but not defined. The TR1 specification comments (N1836 §3.7.2.6):
These member functions shall be left undefined.
[Note: the boolean-like conversion opens a loophole whereby two function instances can be compared via
==
or!=
. These undefinedvoid
operators close the loophole and ensure a compile-time error. —end note]
My understanding of the "loophole" is that if we have a bool
conversion function, that conversion may be used in equality comparisons (and in other circumstances):
struct S {
operator bool() { return false; }
};
int main() {
S a, b;
bool are_equal(a == b); // Uses operator bool on a and b! Oh no!
}
I was under the impression that the safe-bool idiom in C++03 and the use of an explicit conversion function in C++11 was used to avoid this "loophole." Boost and TR1 both use the safe-bool idiom in function
and C++11 makes the bool
conversion function explicit.
As an example of a class that has both, std::shared_ptr
both has an explicit bool
conversion function and is equality comparable.
Why is std::function
not equality comparable? What is the "possible hole in the type system?" How is it different from std::shared_ptr
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
std::function
是任意可调用类型的包装器,因此为了实现相等比较,您必须要求所有可调用类型都是相等比较的,这给任何实现者带来了负担一个函数对象。即使这样,您也会得到一个狭窄的相等概念,因为如果(例如)它们是通过以不同顺序绑定参数来构造的,则等效函数将比较不相等。我相信在一般情况下不可能测试等效性。我猜这意味着删除运算符并且确定使用它们永远不会给出有效的代码比证明在某些先前未发现的极端情况下不可能发生不需要的隐式转换更容易。
std::shared_ptr
具有明确定义的相等语义;两个指针相等当且仅当它们都为空或都非空且指向同一个对象时。std::function
is a wrapper for arbitrary callable types, so in order to implement equality comparison at all, you'd have to require that all callable types be equality-comparible, placing a burden on anyone implementing a function object. Even then, you'd get a narrow concept of equality, as equivalent functions would compare unequal if (for example) they were constructed by binding arguments in a different order. I believe it's impossible to test for equivalence in the general case.I would guess this means it's easier to delete the operators, and know for certain that using them will never give valid code, than to prove there's no possibility of unwanted implicit conversions occurring in some previously undiscovered corner case.
std::shared_ptr
has well-defined equality semantics; two pointers are equal if and only if they are either both empty, or both non-empty and pointing to the same object.我可能是错的,但我认为
std::function
对象的相等性在一般意义上是不可解决的。例如:f1
和f2
相等吗?如果我添加任意数量的函数对象,这些函数对象以各种方式简单地相互包装,最终归结为对f
的调用...仍然相等,该怎么办?I may be wrong, but I think that equality of
std::function
objects is unfortunately not solvable in the generic sense. For example:are
f1
andf2
equal? What if I add an arbitrary number of function objects which simply wrap each other in various ways which eventually boils down to a call tof
... still equal?我认为主要原因是,如果是的话,那么它就不能与非相等比较类型一起使用,即使从未执行相等比较。
即执行比较的代码应该尽早实例化 - 当可调用对象存储到 std::function 中时,例如在构造函数或赋值运算符之一中。
这样的限制会大大缩小应用范围,对于“通用多态函数包装器”来说显然是不可接受的。
值得注意的是,可以 将
boost::function
与可调用对象进行比较(但不与另一个boost::function
)这是可能的,因为执行此类比较的函数是根据已知的操作数类型在比较点实例化的。
此外,
std::function
有一个< strong>target
模板成员函数,可以用来进行类似的比较。事实上boost::function
的比较运算符是 根据target
成员函数实现。因此,不存在阻碍 function_comparable 实现的技术障碍。
在答案中存在常见的“一般不可能”模式:
我可能是错的,但不幸的是,我认为
std::function
对象的相等性在一般意义上是不可解决的。因为图灵机的等价性是不可判定的。给定两个不同的函数对象,您不可能确定它们是否计算相同的函数。 [该答案已被删除]
我完全不同意这一点:执行比较本身不是
std::function
的工作;它的工作只是重定向请求以与底层对象进行比较 - 仅此而已。如果底层对象类型没有定义比较 - 这将是一个编译错误;无论如何,都不需要
std::function
来推导比较算法。如果底层对象类型定义了比较,但工作错误,或者有一些不寻常的语义 - 这也不是 std::function 本身的问题,而是底层类型的问题。
可以基于
std::function
实现function_comparable
。这是一个概念验证:
有一个很好的属性 -
function_comparable
可以与std::function
也是。例如,假设我们有
std::function
s 向量,并且我们希望为用户提供register_callback
和unregister_callback
函数。仅unregister_callback
参数需要使用function_comparable
:现场演示 < strong>Ideone
演示源代码:
输出为:
PS 似乎在
std::type_index
,可以实现类似于function_comparable
类的东西,它也支持排序(即std ::less
)甚至是散列。不仅可以在不同类型之间排序,还可以在同一类型内排序(这需要类型的支持,例如LessThanComparable
)。I think main reason is that if it were, then it couldn't be used with non equality comparable types, even if equality comparison is never performed.
I.e. code that performs comparison should be instantiated early - at the time when a callable object is stored into
std::function
, for instance in one of the constructors or assignment operators.Such a limitation would greatly narrow the scope of application, and obviously not be acceptable for a "general-purpose polymorphic function wrapper".
It is important to note that it is possible to compare a
boost::function
with a callable object (but not with anotherboost::function
)This is possible, because function that performs such comparison is instantiated at point of comparison, based on known operand types.
Moreover,
std::function
has atarget
template member function, which can be used to perform similar comparison. In factboost::function
's comparison operators are implemented in terms oftarget
member function.So, there are no technical barriers which block implementation of
function_comparable
.Among answers there is common "impossible in general" pattern:
I completely disagree with this: it is not the job of
std::function
to perform comparison itself; its job is just to redirect request to comparison to underlying objects - that's all.If underlying object type does not define comparison - it will be a compilation error; in any case,
std::function
is not required to deduce a comparison algorithm.If the underlying object type defines comparison, but which works wrongly, or has some unusual semantic - it is not the problem of
std::function
itself either, but it is problem of the underlying type.It is possible to implement
function_comparable
based onstd::function
.Here is a proof-of-concept:
There is a nice property -
function_comparable
can be compared againststd::function
too.For instance, let's say we have vector of
std::function
s, and we want to give usersregister_callback
andunregister_callback
functions. Use offunction_comparable
is required only forunregister_callback
parameter:Live demo at Ideone
Source code of demo:
Output is:
P.S. It seems that with help of
std::type_index
, it is possible to implement something similar tofunction_comparable
class, which also supports ordering (i.e.std::less
) or even hashing. Not only ordering between different types, but also ordering within same type (this requires support from types, likeLessThanComparable
).根据 http://www.open- std.org/jtc1/sc22/wg21/docs/lwg-active.html#1240:
在 C++11 中,删除的函数被认为是多余的,因为引入了显式转换运算符,因此它们可能会在 C++11 中删除。
至于为什么不能比较 std::function 对象,可能是因为它们可能保存全局/静态函数、成员函数、仿函数等,并且要执行此操作
std::函数“擦除”有关底层类型的一些信息。因此,实现相等运算符可能是不可行的。
According to http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#1240:
In C++11, the deleted functions are considered superfluous with the introduction of explicit conversion operators, so they will probably be removed for C++11.
As for why you can't compare
std::function
objects, it's probably because they can possibly hold global/static functions, member functions, functors, etc, and to do thatstd::function
"erases" some information about the underlying type. Implementing an equality operator would probably not be feasible because of that.其实,你可以比较目标。它可能有效取决于您想要从比较中得到什么。
这里的代码带有不等式,但您可以看到它是如何工作的:
Ups,它仅从 C++11 起有效。
Actually, you can compare targets. It may work depends of what you want from comparison.
Here the code with inequality, but you can see how it works:
Ups, it is only valid since C++11.
尝试下面的方法怎么样,这对于测试模板很有效。
How about trying something like the following, this works well for testing templates.
至少可以做的是,如果 std::function 保存用于绑定到字符串的函数的地址并改为使用字符串比较。
the least that could be done is if std::function saves the address of the function used for binding to a string and used string comparison instead.