但为什么会结合这样的方式设计,这编译:
auto g = std::bind(&Foo::baz, &foo);
我可以叫f
,但我永远不能叫g
。为什么要编译?
的Boost.Bind FAQ说Boost.Bind通常会在诊断“绑定时间”这样的错误(即你在哪里打电话bind
行)。然而,标准并没有要求对std::bind
,而不是它在以下需要std::bind
元件:
INVOKE (fd, w1, w2, ..., wN)
(20.9.2)应为某些值W1有效的表达式,W2, ...,wN,其中N == sizeof...(bound_args)
。
这意味着你的代码违反了函数的前提条件,这会导致未定义的行为。标准库的实现没有义务检查违反先决条件,这是你的工作。这个库也不禁止检查它们,所以它会符合一个实现来拒绝它,就像Boost.Bind一样。我会向图书馆供应商提出请求,要求他们在可能的情况下诊断无效的绑定表达式,作为“实施质量”增强功能。
为什么不只是默认通所有按照正确的顺序的参数,而不必指定呢?
我能想到两个原因。
首先,通过bind
创建的调用包装的行为是丢弃不对应于一个占位符参数,所以你可以调用x(1, 2, 3)
并让它忽略所有的参数,并调用foo.bar()
。这是一种通用模式的一部分,您可以使用bind
来包装一个N-arity函数,以创建一个完全不同的元素的调用包装器,该元素可能会添加参数,删除它们,修复某些特定的绑定值等。如果在绑定表达式中未使用占位符时的默认行为是转发所有参数,则不可能使所有参数都被删除。
其次,总是要求你明确哪些参数要以哪个顺序传递,这更加一致。一般来说,只有在没有绑定参数的情况下传递所有调用参数才有意义,否则bind
应该如何知道是否在绑定参数之前或之后传递调用参数?
例如鉴于
struct X {
void f(int, int) { }
} x;
auto h = bind(&X::f, &x, 1);
h(2);
应x.f(1, 2)
或x.f(2, 1)
调用h(2)
结果呢?由于当存在绑定参数时的正确行为并不明显,所以当没有占位符时,自动转发所有参数只有在没有绑定参数时才有意义(因为不存在绑定参数应该先出现还是后出现的问题) ,这是一个相当特殊的情况。改变API的一个重要特性来处理这种特殊情况将具有可疑价值,尤其是当它使得不可能实现的情况下。
另一种解决方案是继续要求用户明确他们想要的内容,但提供一个明确的方式来表达TomaszKamiński在N4171中提出的“只转发所有内容”,这将在接下来的C++委员会会议上讨论周。该_all
占位解决决定调用参数是否应该来绑定参数之前或之后,因为你可以明确地说,你是否想问题bind(f, arg1, arg2, std::placeholders::_all)
或bind(f, std::placeholders::_all, arg1, arg2)
甚至bind(f, arg1, std::placeholders::_all, arg2)
大概是因为没有办法得到的参数个数这意味着生成的函数对象必须允许*任意数量的参数,包括none,如果(例如)'g'被“调用”而没有参数,会导致未定义的行为。 – 2014-10-10 11:36:11
我认为'std :: placeholders'执行两个函数,1.如你所说,它们将参数从源代码传递到汇编代码,2.它们表示需要多少个参数。 – Niall 2014-10-10 11:40:57
boost库可能有更多与“bind”相关的原始设计原理。 – Niall 2014-10-10 11:41:51