Безопасно ли использовать boost :: bind для передачи большего количества аргументов, чем ожидалось? - PullRequest
24 голосов
/ 13 сентября 2011

Используя , результирующая может получить больше аргументов, чем ожидает связанный объект.Концептуально:

int func() { return 42; }
boost::function<int (int,int,int)> boundFunc = boost::bind(&func);
int answer = boundFunc(1,2,3);

В этом случае func() получает 1,2 и 3 в стеке, хотя его сигнатура указывает на отсутствие аргументов.

Это отличается от болеетипичное использование boost::bind для частичное применение , где значения фиксированы для определенных объектов, что дает boost::function, который принимает меньше аргументов, но предоставляет правильное количество аргументов при вызове связанного объекта.

Следующий код работает с MSVC ++ 2010 SP1.Это сокращенная форма для публикации;исходный код также работает в g ​​++ 4.4 в Linux.

Является ли следующее хорошо определенным в соответствии со стандартом C ++?

#include <iostream>
#include <boost/bind.hpp>
#include <boost/function.hpp>

using namespace std;

void func1(int x) { std::cout << "func1(" << x << ")\n"; } // end func1()

void func0() { std::cout << "func0()\n"; } // end func0()

int main(int argc,char* argv[]) 
{
        typedef boost::function<void (int)> OneArgFunc;
        OneArgFunc oneArg = boost::bind(&func1,_1);
        // here we bind a function that accepts no arguments
        OneArgFunc zeroArg = boost::bind(&func0);
        oneArg(42);
        // here we invoke a function that takes no arguments 
        // with an argument. 
        zeroArg(42);

   return 0;
} // end main()

Я понимаю, почему zeroArg(42) работает: неиспользуемый аргумент помещается в стек вызывающей подпрограммой и просто не вызывается вызываемой подпрограммой.Когда вызываемая подпрограмма возвращается, вызывающая подпрограмма очищает стек.Поскольку он помещает аргументы в стек, он знает, как их удалить.

Не нарушит ли это переход на другую архитектуру или компилятор?Разве более агрессивная оптимизация сломает это?


Я ищу более сильное утверждение либо из документации Boost, либо из документа стандартов.Мне также не удалось найти однозначную позицию.

Используя отладчик и просматривая сборку и стек, становится ясно, что func из первого примера не получает значения 1,2 и 3: вы правы в этом отношении.То же самое верно для func0 во втором примере.Это верно, по крайней мере, для тех реализаций, на которые я смотрю, MSVC ++ 2010SP1 и g ++ 4.4 / Linux.

Глядя на ссылочную документацию Boost, это не так ясно, как хотелось бы, чтобы это былобезопасно передавать дополнительные аргументы:

bind(f, _2, _1)(x, y);                 // f(y, x)

bind(g, _1, 9, _1)(x);                 // g(x, 9, x)

bind(g, _3, _3, _3)(x, y, z);          // g(z, z, z)

bind(g, _1, _1, _1)(x, y, z);          // g(x, x, x)

Обратите внимание, что в последнем примере объект функции, созданный bind(g, _1, _1, _1), не содержит ссылок на какие-либо аргументы, кроме первого, но он все еще может бытьиспользуется с более чем одним аргументом. Любые дополнительные аргументы игнорируются, как первый и второй аргументы игнорируются в третьем примере. [Выделение мое.]

ОператорПро игнорирование дополнительных аргументов не так однозначно, как я хотел бы убедить меня, что это верно в общем случае.Глядя на TR1 , из раздела 3.6.3 становится ясно, что вызываемый объект , возвращаемый из bind, может быть вызван с другим числом аргументов, чем целевой объект ожидает.Это лучшая гарантия?

1 Ответ

15 голосов
/ 13 сентября 2011

Да, это безопасно и портативно & ndash; как явно указано в документации , выражение привязки, возвращаемое boost::bind, молча игнорирует дополнительные аргументы.

Т.е. в первом примере func не не получает значения 1, 2 и 3 & ndash; boundFunc получает значения 1, 2 и 3 и перенаправляет их в содержащееся выражение привязки, которое безопасно получает и игнорирует их, а затем вызывает func(). Аналогично, во втором примере zeroArg получает значение 42 и перенаправляет его в содержащееся в нем выражение связывания, которое получает и игнорирует значение, а затем вызывает func0().

...