c++ - 为什么 std::bind 不考虑功能元数?

标签 c++ c++11

如果我有这个简单的案例:

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

这会编译是有道理的:

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

但是为什么 bind 会被设计成这样:

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

我可以调用f,但我永远不能调用g。为什么还要进行编译?要求我必须这样做的理由是什么:

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

如果你想弄乱哪些参数被传递以及以什么顺序传递,我可以理解使用占位符,但为什么不让默认传递所有参数无需指定的正确顺序?

最佳答案

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 FAQ表示 Boost.Bind 通常会在“绑定(bind)时间”(即在您调用 bind 的那一行)诊断此类错误。然而,标准并不要求 std::bind,而是在 std::bindRequires 元素中具有以下内容:

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 所做的那样。我会向您的库供应商提出请求,要求他们在可能的情况下诊断无效的绑定(bind)表达式,作为“实现质量”的增强。 (编辑:我让 libstdc++ 的 bind 这样做,从 GCC 5 开始。)

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 元函数,以创建具有完全不同元数的调用包装器,该包装器可能会添加参数、删除参数、将某些参数固定为特定绑定(bind)值等. 如果在绑定(bind)表达式中未使用占位符时的默认行为是转发所有参数,则不可能让 x(1, 2, 3) 删除所有参数。

其次,始终要求您明确说明要以何种顺序传递哪些参数更为一致。一般来说,只有在没有绑定(bind)参数的情况下传递所有调用参数才有意义,否则 bind 应该如何知道是在绑定(bind)参数之前还是之后传递调用参数?

例如给予

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

h(2) 的调用结果应该是 x.f(1, 2) 还是 x.f(2, 1)?由于存在绑定(bind)参数时的正确行为并不明显,因此在没有使用占位符时自动转发所有参数仅在没有绑定(bind)参数时才真正有意义(因为绑定(bind)参数应该放在最前面还是最后) ,这是一个相当特殊的情况。更改 API 的重要功能以处理该特殊情况的值(value)值得怀疑,尤其是当它使 x(1, 2, 3) -> foo.bar() 无法实现的情况。

另一种解决方案是继续要求用户明确说明他们想要什么,但提供一种明确的方式来表达“只转发所有内容”,正如 Tomasz Kamiński 在 N4171 中提出的那样这将在下周的 C++ 委员会 session 上讨论。 _all 占位符解决了决定调用参数应该出现在绑定(bind)参数之前还是之后的问题,因为你可以明确地说你是否想要 bind(f, arg1, arg2, std::placeholders::_all)bind(f, std::placeholders::_all, arg1, arg2) 甚至 bind(f, arg1, std::placeholders::_all, arg2)

关于c++ - 为什么 std::bind 不考虑功能元数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31189812/

相关文章:

c++ - 从全局静态变量切换到静态变量会破坏代码

c++ - 在 Qt、QtCreator 和 QMake 中配置 GCC 编译器开关

c++ - 函数返回 constexpr 不编译

c++ - 寻找 map 技术

c++ - 遍历字符串时尝试访问子字符串的编译器错误

c++ - 光线追踪器中的错误紫外线

c++ - C++ 编译器如何找到外部变量?

java - 如何添加到文件中(在顶部添加)

C++强制覆盖

C++11 Lambda 通过捕获传递