为什么从bind返回的对象会忽略额外的参数?

22

假设我有一个接受两个参数的函数,

void f(int x, int y);

我希望绑定其中一个。我可以使用std::bind,如下所示:

auto partiallyBoundF = std::bind(f, 10, _1);

partiallyBoundF函数只接受一个参数,但是我可以用多个参数调用它。除了第一个参数之外的其他参数甚至不必是有意义的类型:

partiallyBoundF(20, 0);
partiallyBoundF(0, 44, -99, "Hello", 4.5, true, []{});

允许从bind返回的对象传递额外参数的目的是什么?它允许调用在其他任何地方都会被拒绝的编译错误。


3
什么编译器?我猜这可能只是一个不符合规范的编译器(因为允许使用这些额外的参数真的没有意义,而且我怀疑标准是不允许这样的)。例如,MSVC通过将每个可变参数模板定义为使用最大可能的模板参数并将其默认为某些NIL类型来模拟可变参数模板。也许类似的事情导致了你的行为? - Christian Rau
3
@ChristianRau 的话是指这是标准库的一部分。这也是 TR1 的一部分。20.8.2/4 中的注释说明,预期实现的变长模板化 operator() 应该接受任何传递给它的参数,不论类型或数量。TR1 有类似的措辞。 - KnowItAllWannabe
1
在您的情况下,f(w1, ..., wN) 其中 N = sizeof...(bound_args)(绑定调用的参数数量)应该是一个有效的表达式,请参见 20.8.9.1.2/2 和 20.8.2/1。编辑:它并不排除任何其他调用方式。 - dyp
1
@KnowItAllWannabe 它允许使用“任意参数列表”,但是“将参数传递给包装的可调用对象”。我认为这种调用方式是否被允许是有争议的(毕竟,f(int, double, whatever)是无效的)。 - dyp
@ChristianRau:MSVC11与11月CTP一起使用时,可以无需额外的投诉接受额外的参数。 - KnowItAllWannabe
@KnowItAllWannabe:是的,微软很清楚地表示,尽管11月CTP包括可变参数模板,但他们还没有更新标准库来使用它们。我不明白为什么人们总是搞错这个问题;当他们谈论CTP时,他们一直强调这一点。 - Nicol Bolas
1个回答

19

忽略额外的参数实现起来更简单,实际上也可能很有用。

在典型的实现中,例如libstdc++(g++),采用的方法是将operator()的参数收集到一个元组中,然后让std::placeholder绑定参数并根据需要提取它们。 强制要求参数数量将需要计算已使用的占位符数量,这将非常复杂。 注意,绑定可调用对象可以是具有多个或模板化的operator()调用模式的函数对象,因此无法生成带有单个“正确”签名的绑定对象operator()

另请注意,您可以编写:

std::bind(&foo, std::placeholders::_1, std::placeholders::_3);

即明确忽略绑定对象的第二个参数。如果bind强制其参数数量,您将需要另一种方式来指定例如第四个参数也将被忽略。

至于有用性,请考虑将成员信号处理程序绑定到信号中:

sig.connect(std::bind(&C::on_sig, this, param, std::placeholders::_1));
如果sig有额外的不需要的发射参数,则这些参数将被bind对象简单地忽略;否则,为多个信号绑定相同的处理程序将需要编写多个转发包装器,而这没有实际目的。

3
“更有用”- 我会对此提出质疑。这会破坏强类型。如果需要的话,这种行为仍然可以通过使用一个接受可变参数的代理函数对象来模拟严格的std :: bind - Konrad Rudolph
2
@KonradRudolph 通常情况下,如果其可调用对象是一个具有多个operator()的函数对象,则绑定表达式在一般情况下没有明确定义的类型。 - ecatmur
您可以使用lambda忽略第四个参数。虽然不太好看。 - dyp
3
实际上,即使对于一个严格的 std::bind 表达式,在一般情况下,其有效调用签名集合也是不可计算的,这是通过明显的构造实现的。 - ecatmur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接