Почему std :: function может неявно преобразовывать в std :: function с большим количеством параметров?

У меня есть следующие :

void print_str(std::shared_ptr<std::string> str) {
    std::cout << str->c_str() << std::endl;
}

int main() {
    auto str = std::make_shared<std::string>("Hello");
    std::function<void()> f = std::bind(print_str, str);

    f(); // correctly print: Hello

    return 0;
}

Я думаю, что тип std::bind(print_str, str) std::function<void(std::shared_ptr<std::string>)>, но приведенный выше код работает правильно. Есть ли прикол в std::bind?

env: centos, gcc82


person Griyn    schedule 08.07.2020    source источник
comment
Нет, это не тип std::bind(print_str, str). В конце концов, результирующая функция не принимает никаких параметров, единственный параметр исходной функции был привязан, так откуда же взялся бы этот второй параметр?   -  person Sam Varshavchik    schedule 08.07.2020


Ответы (3)


То, что делает std :: bind, правильно. Он использует указанное вами значение (str) для вызова print_str. Таким образом, вам больше не нужно указывать его, и он всегда будет заменен связанным значением.

#include <iostream>
#include <functional>

int sum(int value1, int value2) {
    return value1 + value2;
}

int main() {

    std::function<int(int, int)> f1 = std::bind(sum, std::placeholders::_1, std::placeholders::_1);
    std::function<int(int)> f2 = std::bind(sum, 10, std::placeholders::_1);
    std::function<int()> f3 = std::bind(sum, 100, 200);
    std::function<int(int)> f4 = std::bind(sum, std::placeholders::_1, 200);

    int a = 1;
    int b = 2;

    std::cout << "the sum of " << a << " and " << b << " is: " << f1(a, b) << std::endl;
    std::cout << "the sum of " << 10 << " and " << b << " is: " << f2(b) << std::endl;
    std::cout << "the sum of " << 100 << " and " << 200 << " is: " << f3() << std::endl;
    std::cout << "the sum of " << 200 << " and " << b << " is: " << f4(b) << std::endl;

    return 0;
}

выход:

the sum of 1 and 2 is: 2
the sum of 10 and 2 is: 12
the sum of 100 and 200 is: 300
the sum of 200 and 2 is: 202

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

f2 связывает одно значение и один заполнитель и возвращает функцию, подобную int(int)

f3 связывает два значения без заполнителя и возвращает функцию, похожую на int()

f4 похож на f2, за исключением того, что заполнитель теперь является первым параметром, а не вторым.

Ваш код относится к случаю f3.

person PirklW    schedule 08.07.2020
comment
Спасибо за ваш ответ! Это легко понять. После запуска вашего кода у меня возникает еще один вопрос: почему std::function<int(int,int)> f5 = std::bind(sum, 1, 2); скомпилирован правильно? Другими словами, какой тип вывода f в auto f = std::bind(sum, 1, 2);? - person Griyn; 08.07.2020
comment
stl не указывает тип возвращаемого значения std :: bind. auto f имеет тип функтора, и вы можете передавать произвольное количество параметров, но не менее того количества заполнителей, которое вы указали при связывании. Остальные параметры просто не будут использоваться. - person PirklW; 08.07.2020
comment
@Griyn Это довольно интересное наблюдение. Вам следовало просить об этом вместо того, что вы просили на самом деле ;-) Похоже, std::bind имеет некоторые непредсказуемые функции: stackoverflow.com/q / 40547846/9883438 - person sebrockm; 08.07.2020
comment
@sebrockm Спасибо! Это также помогает разрешить мою путаницу. - person Griyn; 09.07.2020

Я думаю, что тип std::bind(print_str, str) std::function<void(std::shared_ptr<std::string>)>

Нет, тип std::bind(print_str, str) - это неуказанный тип функтора, что-то вроде

class binder
{
    void(*f)(std::shared_ptr<std::string>);
    std::shared_ptr<std::string> p;
public:
    template<typename... Args>
    void operator()(Args... ) { f(p); }
};

Обратите внимание, что это можно вызвать с любыми аргументами или без них.

person Caleth    schedule 08.07.2020

То, что вы здесь переживаете, правильно и делает именно то, для чего std::bind был разработан.

Проще говоря: он превращает функцию, принимающую n параметров, в функцию, принимающую m параметров (где n >= m). В вашем конкретном случае вы даете ему функцию, принимающую один параметр, и возвращаете функцию, принимающую нулевые параметры. Эта новая функция будет внутренне вызывать print_str и всегда передавать str в качестве аргумента.

Примечание:

Поскольку в C ++ 11 есть лямбда-выражения, std::bind в некотором роде излишне. То, что вы делаете, в точности эквивалентно этому:

void print_str(std::shared_ptr<std::string> str) {
    std::cout << str->c_str() << std::endl;
}

int main() {
    auto str = std::make_shared<std::string>("Hello");
    std::function<void()> f = [=]() { print_str(str); };

    f(); // correctly print: Hello

    return 0;
}

Надеюсь, это также поможет понять, что std::bind делает за кулисами.

person sebrockm    schedule 08.07.2020