Почему объекты, возвращенные из bind, игнорируют дополнительные аргументы?

18

Предположим, что у меня есть функция, которая принимает два аргумента,

void f(int x, int y);

, и я хочу связать один из них. Я могу использовать std::bind следующим образом:

auto partiallyBoundF = std::bind(f, 10, _1);

partiallyBoundF принимает только один аргумент, но я могу назвать его более чем одним. Аргументы, выходящие за пределы первого, даже не должны иметь тип, который имеет какой-либо смысл:

partiallyBoundF(20, 0);
partiallyBoundF(0, 44, -99, "Hello", 4.5, true, []{});

Какова цель разрешения объектов, возвращенных из bind , передать дополнительные аргументы? Он позволяет вызывать ошибки для компиляции, которые будут отклонены в любом месте.

    
задан KnowItAllWannabe 06.11.2012 в 14:22
источник
  • Какой компилятор? Я предполагаю, что это может быть просто несоответствующий компилятор (потому что разрешение этих дополнительных аргументов действительно не имеет смысла, и я сомневаюсь, что стандарт позволяет это). Например, MSVC эмулирует вариационные шаблоны, просто определяя каждый вариационный шаблон, чтобы принимать максимально возможные аргументы шаблона и по умолчанию использовать их для некоторого типа NIL. Может быть, что-то вроде этого является причиной вашего поведения? –  Christian Rau 06.11.2012 в 14:30
  • @ChristianRau: Это часть стандарта. Это тоже было частью TR1. 20.8.2 / 4 комментирует, что ожидаемая реализация предназначена для вариативно-шаблонного оператора (), чтобы просто взять все, что передается ему, независимо от типа или количества аргументов. TR1 имеет аналогичную формулировку. –  KnowItAllWannabe 06.11.2012 в 14:44
  • В вашем случае f (w1, ..., wN), где N = sizeof ... (bound_args) (количество аргументов для привязки вызова) должно быть допустимым выражением, см. 20.8.9.1.2 / 2 и 20.8.2 / 1. Изменить: он НЕ запрещает другой способ его вызова. –  dyp 06.11.2012 в 14:45
  • @KnowItAllWannabe Позволяет принимать «список произвольных аргументов», но «доставляет аргументы обернутому вызываемому объекту». Я думаю, что можно утверждать, что позволяет ли этот вид вызовов (в конце концов, F (INT, двойной, что угодно) не действует). –  dyp 06.11.2012 в 15:00
  • @ChristianRau: MSVC11 с ноябрьским CTP принимает дополнительные аргументы без жалобы. –  KnowItAllWannabe 06.11.2012 в 16:42

1 ответ

16

Игнорирование дополнительных аргументов намного проще для реализации и может быть полезно.

В типичной реализации, например, libstdc ++ (g ++), подход состоит в том, чтобы собрать аргументы operator() в кортеж, а затем позволить аргументам связывания std::placeholder извлекать их по мере необходимости. Принудительный подсчет аргументов потребует подсчета количества используемых заполнителей, что было бы довольно сложно. Обратите внимание, что вызываемый bind может быть функтором с шаблонами вызовов с несколькими шаблонами или шаблонами operator() , поэтому объект bind operator() не может быть сгенерирован с помощью одной «правильной» подписи.

Также обратите внимание, что вы можете написать:

std::bind(&foo, std::placeholders::_1, std::placeholders::_3);

то есть. явно игнорируя второй аргумент для объекта bind. Если bind принудительно подсчитал свой аргумент, вам понадобится дополнительный способ указать, например, четвертый аргумент также следует игнорировать.

Что касается полезности, рассмотрите привязку обработчика сигнала элемента к сигналу:

sig.connect(std::bind(&C::on_sig, this, param, std::placeholders::_1));

Если sig имеет дополнительные параметры нежелательного излучения, то они просто игнорируются объектом bind ; в противном случае привязка одного и того же обработчика к нескольким сигналам потребует написания нескольких оберток пересылки для реальной цели.

    
ответ дан ecatmur 06.11.2012 в 14:58
  • «более полезно» - я бы обсудил это. Это нарушает сильную типизацию. И при желании это поведение можно было бы эмулировать со строгим std :: bind, просто используя прокси-функтор, который принимает вариативные аргументы. –  Konrad Rudolph 06.11.2012 в 15:03
  • @KonradRudolph выражение bind не имеет четко определенного типа в общем случае, если его вызываемый является функтором с несколькими операторами () s. –  ecatmur 06.11.2012 в 15:08
  • Вы можете игнорировать четвертый аргумент, используя лямбда. Не очень приятно. –  dyp 06.11.2012 в 15:13
  • @KonradRudolph на самом деле, набор действительных сигнатурных сигнатур выражения привязки не является вычислимым в общем случае с очевидной конструкцией даже для строгой std :: bind. –  ecatmur 06.11.2012 в 15:20
  • @ecatmur Это отличная (но совершенно другая) точка. –  Konrad Rudolph 06.11.2012 в 15:40