Std:: bind не имеет для меня никакого смысла

Следующее:

#include <functional>

struct Foo 
{
    void bar1() {}
    void bar2(int) {}
    void bar3(int, int) {}
    void bar4(int, int, int) {}
};


int main()
{
    Foo foo;

    auto f1 = std::bind(&Foo::bar1, &foo);
    auto f2 = std::bind(&Foo::bar2, &foo);
    auto f3 = std::bind(&Foo::bar3, &foo);
    auto f4 = std::bind(&Foo::bar4, &foo);

    f1(1, 2, 3, 4); // success
    f2(1, 2, 3);    // error
    f3(1, 2);       // error
    f4(1);          // error    

    return 0;
}

f1 (1, 2, 3, 4) компилирует и выполняет bar1(). f2 (1, 2, 3), не компилируется, f3 (1, 2) не компилируется (но bar3 имеет правильный прототип), а f4 (1) не компилируется. Ошибка, которую я получаю с Visual Studio 2013 для этих трех случаев, -

"класс не определяет" operator() "или пользовательский оператор преобразования к указателю на функцию или ссылку на функцию, которая принимает соответствующее количество аргументов"

У меня ограниченное понимание шаблонов и стандартной библиотеки, но это, похоже, не имеет для меня никакого логического смысла. Есть ли достаточно простое объяснение?

Ответ 1

Чтобы передать аргументы цели, вам необходимо предоставить эти аргументы в выражении привязки или оставить их несвязанными, добавив заполнители в выражение привязки, а затем вы должны вызвать функцию с аргументами, которые будут заменены заполнителями.

Вы можете вызвать bar1 без заполнителей, потому что он не принимает никаких аргументов, поэтому вам не нужно ничего передавать. Аргументы, которые вы передаете в f1, просто игнорируются, потому что нет несвязанных аргументов, т.е. Нет заполнителей, которые нуждаются в замене.

Другие функции требуют аргументов, поэтому вы должны либо связывать аргументы с "временем связывания", например.

auto f2a = std::bind(&Foo::bar2, &foo, 1);
f2a();

или оставить аргументы несвязанными и предоставить их при вызове вызываемого объекта:

auto f2b = std::bind(&Foo::bar2, &foo, std::placeholders::_1);
f2b(1);

Обратите внимание, что GCC 5 теперь имеет статические утверждения, чтобы поймать такую ​​ошибку. Если арность целевой функции может быть определена, и выражение привязки не имеет ни связанного аргумента, ни заполнителя для каждого параметра целевой функции, тогда он говорит:

/usr/local/gcc-head/include/С++/5.0.0/functional: 1426: 7: ошибка: статическое утверждение не выполнено: неправильное количество аргументов для указателя на элемент

То, что вы написали, эквивалентно этому, используя lambdas вместо bind:

Foo foo;

auto f1 = [&foo](...) { return foo.bar1(); };
auto f2 = [&foo](...) { return foo.bar2(); };
auto f3 = [&foo](...) { return foo.bar3(); };
auto f4 = [&foo](...) { return foo.bar4(); };

f1(1, 2, 3, 4); // success
f2(1, 2, 3);    // error
f3(1, 2);       // error
f4(1);          // error   

то есть. вы определяете функторы, которые будут принимать любые аргументы, но игнорировать их, а затем вызывать функции-члены Foo, но не обязательно с правильными аргументами.

Ответ 2

Кажется, у вас есть непонимание того, что должен делать bind.
Аргументы, которые вы передаете в результате bind, "замещаются" в заполнителях _1, _2 и т.д. Аргументы, которые не соответствуют местозаполнителю, являются, ну, неиспользованными и поэтому игнорируются. Поскольку вы не используете какие-либо заполнители в выражениях связывания, внутренний вызов bar1 всегда эквивалентен foo.bar1() - поэтому он не зависит от аргументов, которые вы передаете в результате bind.

Теперь становится ясно, что вызов bar4 эквивалентен foo.bar4(), но bar4 ожидает четыре аргумента, так что вздор. Вы можете исправить это, написав

using namespace std::placeholders;
auto f4 = std::bind(&Foo::bar4, &foo, _1, _2, _3, _4);

Теперь, если вы предоставите четыре аргумента, они будут переданы правильно, а внутренний вызов станет эквивалентным, например, foo.bar4(1, 2, 3, 4).