Могу ли я привязаться к функции, которая принимает аргументы по умолчанию, а затем вызвать ее?

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

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

int main() {
  auto f = std::bind(foo, 23, 34); // works
  f();


  auto g = std::bind(foo, 23); // doesn't work
  g();

  using std::placeholders::_1;
  auto h = std::bind(foo, 23, _1); // doesn't work either 
  h();

}

person Stephan Dollberg    schedule 23.05.2012    source источник
comment
Определить не работает. Код будет скомпилирован, если вы дадите переменным разные имена.   -  person R. Martinho Fernandes    schedule 23.05.2012
comment
почему вы продолжаете переназначать f?   -  person 111111    schedule 23.05.2012
comment
@R.MartinhoFernandes, да, извините, пример был неполным. обновленный код   -  person Stephan Dollberg    schedule 23.05.2012
comment
@ 111111 это было скорее символическое значение. обновленный код   -  person Stephan Dollberg    schedule 23.05.2012


Ответы (4)


По сути, каждый раз, когда вы пишете foo(x), компилятор переводит его в foo(x, 23);. Это работает только в том случае, если у вас действительно есть прямой вызов с именем функции. Вы не можете, например, присвоить &foo void(*)(int), потому что сигнатура функции void(int, int). Параметры по умолчанию не играют никакой роли в подписи. И если вы присвоите его переменной void(*)(int, int), информация о параметре по умолчанию будет потеряна: вы не сможете использовать параметр по умолчанию через эту переменную. std::bind хранит void(*)(int, int) где-то в своих недрах и, таким образом, теряет информацию о параметрах по умолчанию.

В C++ нет способа получить значение параметра по умолчанию извне функции, поэтому вам приходится вручную указывать значение по умолчанию при привязке.

person R. Martinho Fernandes    schedule 23.05.2012

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

Что-то в этом роде:

auto g = [] (){ foo( 23 ); };

РЕДАКТИРОВАТЬ: только что проверил и работает нормально: http://ideone.com/SPSvi

person obmarg    schedule 23.05.2012

У меня есть два решения:

1 - Вы можете перегрузить foo() и заставить его вызывать оригинал со значениями по умолчанию:

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

inline void foo(int a)
{
    foo(a, 23);
}

2 - Вы можете использовать статическую переменную по умолчанию, а затем использовать ее в процессе привязки:

static int foo_default_b = 23;
void foo(int a, int b = foo_default_b) 
{
    std::cout << a << " " << b << std::endl;
}

auto g = std::bind(foo, 23, foo_default_b);
g();
person Asaf    schedule 23.05.2012

Этот ответ не согласуется с ответом Р. Мартиньо Фернандеса. Вы действительно можете использовать boost::bind для привязки к параметрам по умолчанию, вам просто нужно вставить заполнители, например:

boost::bind<void (int, int)>(foo, _1, _2)(12);

Это вызовет foo(12, 23), как и ожидалось. Хотя я не тестировал этот конкретный код, я сделал что-то подобное в своем коде на основе ответа, указанного выше, и он работает в gcc 4.8.5.

Хм, я только что заметил, что это вопрос о std::bind, а не boost::bind. Я не знаю, есть ли отличия, если они есть.

person Trebor Rude    schedule 23.12.2015
comment
Хотя я не тестировал этот конкретный код, он объясняет, почему он не работает в GCC 4.8.5 (или любой другой версии) с любой версией Boost (от 1.49.0 до 1.64.0). Этот ответ, на который вы ссылались, не говорит, что вы можете вызывать выражение привязки без параметров по умолчанию, а скорее объясняет, что вы можете либо привязать конкретное значение, либо требуется передать аргумент. Ответ Р. Мартиньо Фернандеса правильный, вы не можете вызывать выражение привязки таким образом (ни используя boost:bind, ни std::bind). - person monkey0506; 25.05.2018