为什么不std :: bind帐户的功能arity? [英] Why doesn't std::bind account for function arity?

查看:189
本文介绍了为什么不std :: bind帐户的功能arity?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我有这个简单的情况:

If I have this simple case:

struct Foo 
{
    void bar();
    void baz(int );
};

这将是有意义的:

Foo foo;
auto f = std::bind(&Foo::bar, &foo);

但是为什么 bind 一种编译方式:

But why would bind be designed in such a way that this compiles:

auto g = std::bind(&Foo::baz, &foo);

我可以调用 f ,但我不能请调用 g 。为什么还要编译?需要我做的事情的理由是什么:

I can call f, but I cannot ever call g. Why even make that compile? What is the rationale behind requiring me to have to do:

auto g2 = std::bind(&Foo::baz, &foo, std::placeholders::_1);

我可以使用占位符来理解,如果你想混乱

I can understand using the placeholders if you want to mess with which arguments get passed and in what order, but why not just have the default pass all the arguments in the right order without having to specify it?

推荐答案


但是为什么要以这样的方式设计bind:

auto g = std :: bind(& Foo :: baz,& foo);

我可以调用 f ,但我不能调用 g 。为什么还要编译?

But why would bind be designed in such a way that this compiles:
auto g = std::bind(&Foo::baz, &foo);
I can call f, but I cannot ever call g. Why even make that compile?

Boost.Bind常见问题说Boost.Bind通常会在绑定时间诊断这样的错误(即在你调用 bind )。然而,标准不需要 std :: bind ,而是在 Requires 元素中具有以下 std :: bind

The Boost.Bind FAQ says that Boost.Bind will usually diagnose such errors at "bind time" (i.e. on the line where you call bind). However the standard doesn't require that for std::bind, instead it has the following in the Requires element for std::bind:


INVOKE(fd,w1,w2,...对于一些值 w1,w2,...,wN ,其中 N = 0,...,wN)(20.9.2) = sizeof ...(bound_args)

INVOKE (fd, w1, w2, ..., wN) (20.9.2) shall be a valid expression for some values w1, w2, ..., wN, where N == sizeof...(bound_args).

这意味着您的代码违反了函数的前提条件在未定义的行为。标准库实现没有义务检查前提条件违反,这是你的工作。该库也不被禁止检查它们,所以它将符合一个实现来拒绝它,如Boost.Bind。我会向您的图书馆供应商提出请求,要求他们诊断无效的绑定表达式,并将其作为实施质量的增强措施。

This means your code violates the function's precondition, which results in undefined behaviour. The standard library implementation is not obliged to check for precondition violations, that's your job. The library isn't forbidden to check them either, so it would be conforming for an implementation to reject it, as Boost.Bind does. I would make a request to your library vendor asking them to diagnose invalid bind expressions where it is possible to do so, as a "Quality of Implementation" enhancement.


为什么不是默认通过全部参数的顺序,而不必指定它?

why not just have the default pass all the arguments in the right order without having to specify it?

我可以想到两个原因。

首先,由 bind 是删除与占位符不对应的参数,因此您可以调用 x(1,2,3)并忽略所有参数并调用 foo.bar()。这是一个通用模式的一部分,你可以使用 bind 包装一个N元素函数来创建一个完全不同的调用包装器,可以添加参数,删除它们,修复一些到特定的绑定值等。如果在绑定表达式中没有使用占位符时的默认行为,删除所有参数是不可能的 x(1,2,3)

Firstly, the behaviour of the call wrapper created by bind is to drop arguments that do not correspond to a placeholder, so you can call x(1, 2, 3) and have it ignore all the arguments and call foo.bar(). This is part of a general pattern where you can wrap an N-arity function using bind to create a call wrapper with a completely different arity that might add arguments, remove them, fix some to specific bound values etc. It would be impossible to have x(1, 2, 3) drop all arguments if the default behaviour when no placeholders are used in the bind expression was to forward all the arguments.

其次,它更一致,总是要求你明确要传递哪些参数。一般来说,当没有绑定参数时传递所有调用参数是有意义的,否则应该如何 bind 知道是否在绑定参数之前或之后传递调用参数?

Secondly, it's more consistent to always require you to be explicit about which arguments you want passed in which order. In general it would only make sense to pass all the invocation arguments when there are no bound arguments, otherwise how should bind know whether to pass the invocation arguments before or after the bound arguments?

struct X {
  void f(int, int) { }
} x;
auto h = bind(&X::f, &x, 1);
h(2);



如果调用 h(2)导致 xf(1,2) xf(2,1)?因为有绑定参数时的正确行为不明显,当没有占位符时自动转发所有参数只有当没有绑定参数时真的有意义(因为没有问题的绑定参数应该是第一还是最后) ,这是一个相当特殊的情况。更改API的一个重要特性以使用该特殊情况将是值得怀疑的,特别是当它使 x(1,2,3) - > foo.bar()无法实现

Should the call to h(2) result in x.f(1, 2) or x.f(2, 1)? Since the correct behaviour when there are bound arguments is not obvious, automatically forwarding all arguments when there were no placeholders used only really makes sense when there are no bound arguments (because then there's no question of whether the bound arguments should come first or last), which is a fairly special case. Changing a significant feature of the API to work with that special case would be of questionable value, especially when it makes the x(1, 2, 3) -> foo.bar() case impossible to achieve.

另一种解决方案是继续要求用户明确他们想要什么,但提供一种明确的方式说只是转发一切,正如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)

An alternative solution is to continue requiring users to be explicit about what they want, but provide an explicit way to say "just forward everything", as proposed by Tomasz Kamiński in N4171 which will be discussed at the C++ committee meeting next week. The _all placeholder solves the problem of deciding whether the invocation arguments should come before or after the bound arguments, because you can explicitly say whether you want bind(f, arg1, arg2, std::placeholders::_all) or bind(f, std::placeholders::_all, arg1, arg2) or even bind(f, arg1, std::placeholders::_all, arg2)

这篇关于为什么不std :: bind帐户的功能arity?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆