С++ 11 привязать и применить?

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?

Другими словами, можно ли написать функцию bind1:

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

, так что когда std::is_placeholder<T>::value == 0 для каждого члена args, bind1() может, в дополнение к тому, что делает std::bind(), вызывать оператор()?


person tinlyx    schedule 21.01.2014    source источник
comment
Привяжите 1 из 3 аргументов, получите функцию 3-1=2 аргумента. Свяжите 2 из 3 аргументов, получите функцию 3-2=1 аргумента. Свяжите 3 из 3 аргументов, получите функцию 3-3=0 аргументов. Было бы удивительно, если бы bind иногда возвращал объект функции, а иногда возвращал что-то еще, особенно если вы создаете функцию, которую можно вызвать для ее побочных эффектов.   -  person user2357112 supports Monica    schedule 21.01.2014
comment
Я так не думаю. Bind делает то, что говорит название. Это связывает. Он не выполняется. Вы просто получаете функтор с меньшим количеством параметров. Это простое, нетехническое описание происходящего.   -  person TimDave    schedule 21.01.2014
comment
время, чтобы решить, что ваш вопрос... вам действительно нужен ответ на ваш последний вопрос?   -  person Karoly Horvath    schedule 21.01.2014
comment
Да, было бы удобно. В других языках, таких как Haskell, вы можете частично применить параметр к функции, делать это снова и снова,..., когда все параметры применяются, вы получаете значение. Я думаю, что-то подобное было бы полезно.   -  person tinlyx    schedule 21.01.2014
comment
Прелюдия › пусть f x y = x + y ; Прелюдия› (f 1) 2 ; 3   -  person tinlyx    schedule 21.01.2014
comment
Наверное, поэтому bind называется bind, а не curry. Кроме того, вам следует еще раз прочитать документацию по адресу std::bind — вы неправильно его использовать. Вы хотите bind(bind(f,1,_1),"Hi!").   -  person Casey    schedule 21.01.2014
comment
@Кейси Спасибо. Я исправил указанную вами проблему использования привязки. Я предполагаю, что мой вопрос теперь больше о второй части. Как иметь что-то вроде bind, которое может выполнять частичное приложение (или curry, как вы его сформулировали), как в других языках.   -  person tinlyx    schedule 21.01.2014


Ответы (2)


Функция без аргументов — это просто значение в Haskell. Вы не называете его, вы просто используете его. Поскольку нет побочных эффектов, нет и заметной разницы.

В OCaml просто нет функций без параметров, чтобы получить что-то подобное, нужно добавить фиктивный юнит-аргумент.

В С++ не так. C++, в отличие от Haskell и OCaml, поддерживает четкое различие между f и f(). 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;
}

Однако устранение разницы между f и f() только в этом месте, ИМХО, не способствует общей согласованности программирования на C++. Если вам не нравится это различие, уберите его везде (или просто используйте Haskell во благо).

person n. 1.8e9-where's-my-share m.    schedule 21.01.2014
comment
Спасибо @n.m. Я думаю, что apply здесь больше похоже на строгую версию частичного применения, а std::bind — на его ленивую версию. Так что оба по-своему последовательны. В этом смысле довольно странно, что ленивая версия используется по умолчанию в С++. Вероятно, проще реализовать, я думаю. - person tinlyx; 13.02.2014

Нет источников для этого, только мое мнение.

Причина, по которой это не было сделано, заключается в том, что для этого нет причин. Если вы знаете все входные данные для функции, просто вызовите ее.

И если бы вы делали что-то с шаблонами, которые привели к этому, вам все равно пришлось бы писать весь код последовательно. Особый случай здесь потребует только особого случая в другом месте.

person Zan Lynx    schedule 21.01.2014
comment
Я полагаю, что имею в виду другие языки, такие как Haskell или OCaml, где вы получаете значение в конце, когда фиксируете все параметры: Prelude› let f x y = x + y ; Прелюдия› (f 1) 2 ; 3 - person tinlyx; 21.01.2014
comment
Просто любопытно, будет ли приведенное выше несоответствие типов для вариационного программирования? - person tinlyx; 21.01.2014
comment
@TingL: Haskell не имеет побочных эффектов, поэтому не имеет значения, оценивается ли функция в точке, где связаны все параметры, или позже, когда значение используется. На самом деле в Haskell есть ленивые вычисления, поэтому функции обычно не вычисляются в точке, где все их значения связаны. Вы не видите ленивых вычислений в REPL, потому что оператор печати заставляет функцию вычисляться почти немедленно, прежде чем вам в следующий раз нужно будет что-то вводить в REPL. Я ничего не знаю об OCaml (у которого, как мне кажется, есть побочные эффекты). - person Ken Bloom; 21.01.2014
comment
@KenBloom Я думаю, что другие языки, которые не ленивы, также имеют частичное применение, может быть, lisp ??. Думаю, меня больше интересует наличие такого на C++. Для моих целей мне не нужна часть побочных эффектов cout << .... bind — первое, что приходит мне на ум. - person tinlyx; 21.01.2014