【问题标题】:c++11 bind and apply?c++11绑定和应用?
【发布时间】:2014-01-21 02:14:15
【问题描述】:

std::bind 有时被描述为“部分应用程序”。为什么当一个函数的所有参数都被绑定时,函数本身没有被应用?

例如,以下代码不打印任何内容。

#include <functional>
#include <iostream>
using namespace std;
using namespace std::placeholders;

void f(int a,string b) {cout << a << b << endl;};
int main() {
  bind(bind(f,1,_1),"Hi!");
  return 0; 
}

有没有办法编写一个绑定变体,可以在所有参数都固定的情况下应用该函数?

--更新--

我现在从回复中了解到std::bind 并不完全是部分应用程序。所以,在问题的第二部分,我怎么能写像 std::bind 这样的东西,但做部分应用。 我知道bind(bind(f,1,_1),"Hi!")() 将调用最终的0 元函数并返回结果值(在示例中打印1Hi)。是否可以做模板编程在bind的终端情况下调用函数调用operator()?

也就是说,能不能写一个函数bind1

template< class R, class F, class... Args >
bind1( F f, Args... args )

,这样当std::is_placeholder&lt;T&gt;::value == 0args 的每个成员时, bind1() 除了std::bind() 所做的之外,还可以调用operator() 吗?

【问题讨论】:

  • 绑定 3 个参数中的 1 个,得到 3-1=2 个参数的函数。绑定 3 个参数中的 2 个,得到 3-2=1 个参数的函数。绑定 3 个参数中的 3 个,得到一个 3-3=0 个参数的函数。让bind 有时返回一个函数对象,有时返回其他东西会令人惊讶,特别是如果您正在创建函数以便调用它以获取其副作用。
  • 我不这么认为。 Bind 按照名称执行。它绑定。它不执行。你只是得到一个参数更少的仿函数。这是对正在发生的事情的简单、非技术性的描述。
  • 是时候决定你的问题是什么了……你真的需要回答你最后一个问题吗?
  • 是的,会很方便。在 Haskell 等其他语言中,您可以将参数部分应用于函数,一次又一次地执行此操作,...,当应用所有参数时,您会得到一个值。我认为类似的东西会很有用。
  • 我想这就是为什么 bind 被称为 bind 而不是 curry。此外,您应该再次阅读std::bind 上的文档 - 您使用不正确。你想要bind(bind(f,1,_1),"Hi!")

标签: c++ c++11 stdbind


【解决方案1】:

没有参数的函数只是 Haskell 中的一个值。你不调用它,你只是使用它。由于没有副作用,因此没有可观察到的差异。

在 OCaml 中根本没有无参数函数,要获得类似的东西,您需要添加一个虚拟单元参数。

在 C++ 中并非如此。与 Haskell 和 OCaml 不同,C++ 在 ff() 之间保持着明显的区别。 bind 为您提供前者,因为您始终可以通过添加 () 将其转换为后者。您可以为 bind 编写自己的包装器,这很容易做到。反过来会有点困难。

以下是此类包装器的可能实现:

#include <functional>
#include <utility>
#include <iostream>

template <typename T>
struct is_noargs_callable {
  private:
    typedef char(&yes)[1];
    typedef char(&no)[2];

    template<typename U> 
      static yes test(decltype((std::declval<U>())())*);

    template<typename> 
      static no test(...);

  public:
    static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

template <typename T>
struct is_noargs_callable<T()> {
  static const bool value = true;
};

template <typename T>
struct is_noargs_callable<T(...)> {
  static const bool value = true;
};

template <typename T>
auto call_me_if_you_can(T t) -> typename std::enable_if<is_noargs_callable<T>::value, decltype(t())>::type
{
  return t();
}

template <typename T>
auto call_me_if_you_can(T t) -> typename std::enable_if<!is_noargs_callable<T>::value, T>::type
{
  return t; 
}

template <typename... Args>
auto apply(Args&&... args) -> decltype(call_me_if_you_can(std::bind(args...))) {
  return call_me_if_you_can(std::bind(args...));
}

// testing

void foo(int a, int b, int c) { std::cout << "foo(" << a << "," << b << "," << c << ")";  }

int main ()
{
  using namespace std::placeholders;
  std::cout << "zero : " ; apply(foo, _1, _2, _3); std::cout << " : " ; apply(foo, _1, _2, _3)(1,2,3); std::cout << std::endl;
  std::cout << "one  : " ; apply(foo, 1, _1, _2); std::cout << " : " ; apply(foo, 1, _1, _2)(2,3); std::cout << std::endl;
  std::cout << "two  : " ; apply(foo, 1, 2, _1); std::cout << " : " ; apply(foo, 1, 2, _1)(3); std::cout << std::endl;
  std::cout << "three: " ; apply(foo, 1, 2, 3);  std::cout << " : "; /* nothing to test here */ std::cout << std::endl;
}

但是,仅在这一点上消除 ff() 之间的差异并不能恕我直言,这有助于 C++ 编程的整体一致性。如果你不喜欢这种区别,那就到处杀掉它(或者干脆用你的 Haskell 来做个好事)。

【讨论】:

  • 谢谢@n.m。我认为这里的 apply 更像是偏应用的严格版本,而 std::bind 更像是它的惰性版本。所以两者都以自己的方式保持一致。从这个意义上说,惰性版本在 C++ 中是默认的,这有点奇怪。我猜可能更容易实现。
【解决方案2】:

没有任何来源,只是我的看法。

没有这样做的原因是因为没有理由这样做。如果您知道该函数的所有输入,只需调用它即可。

如果您使用导致这种情况的模板进行操作,则无论如何您都需要一致地编写所有代码。这里的特殊情况只需要其他地方的特殊情况。

【讨论】:

  • 我想我正在考虑其他语言,如 Haskell 或 OCaml,当你修复所有参数时,你最终会得到一个值: Prelude> let f x y = x + y ;前奏> (f 1) 2 ; 3
  • 只是好奇,上面会不会产生可变参数编程的类型不一致?
  • @TingL:Haskell 没有副作用,因此无论是在绑定所有参数的点或稍后使用值时评估函数都没有关系。事实上,Haskell 有惰性求值,所以函数通常不会在它们的所有值都被绑定的地方进行求值。您在 REPL 中看不到惰性求值,因为在下一次您必须向 REPL 中输入任何内容之前,强制函数几乎立即被求值的 print 语句。我对 OCaml 一无所知(我认为它确实有副作用)。
  • @KenBloom 我认为其他不懒惰的语言也有部分应用,也许是 lisp??。我想我对在 C++ 中拥有这样的东西更感兴趣。出于我的目的,我不需要副作用部分 cout &lt;&lt; ...。 bind 是我第一个想到的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-12
  • 1970-01-01
相关资源
最近更新 更多