Какого черта делает std::bind(x, y)?
Я застрял, читая описание std::bind
в N3225, в подразделе 20.8.10.1
, Это говорит следующее должно напечатать 1
, но я думал, что bind
должен скопировать свои аргументы и поэтому должен напечатать 0
, Если кто-то хочет сослаться на переданный аргумент, нужно использовать std::ref
, право?
void f(int &a) { a = 1; }
int main() {
int a = 0;
std::bind(f, a)();
std::cout << a << std::endl;
}
Выходы GCC 0
Согласиться с тем, что я думал, что все работает. Но N3225 говорит, что std::bind(f, a1)
должен вернуть упаковщик вызовов, который при вызове wrapper()
позвоню INVOKE(f, v1)
, где v1
должен быть a
(аргумент, который я передал, другими словами, используя binds
входящий параметр, который является идеальным параметром пересылки, std::forward<A1>(a1)
).
INVOKE(f, a)
определяется 20.8.2 для f(a)
, Таким образом, это определяет, что вызов возвращаемой оболочки вызовов передает исходный аргумент. Что мне не хватает?
3 ответа
Вау, это сбивает с толку невероятно. Определяет v1
как tid
и это как следующее (ti
i-й параметр привязки совершенной пересылки, и TiD
является распавшим типом этого параметра - т.е. массив становится указателем и т. д.).
tid
является lvalue типаTiD
построен изstd::forward<Ti>(ti)
Хорошо, я сказал, это tid
является std::forward<Ti>(ti)
и это lvalue! Но это не то, что на самом деле значит сказать. Это значит
tid
является lvalue типаTiD
что относится к объекту, построенному изstd::forward<Ti>(ti)
Теперь это имеет больше смысла. Потому что если std::forward<Ti>(ti)
на самом деле значение? "Lvalue... построенный из..." означает, что мы создаем новый объект из "..." и заставляем lvalue ссылаться на него.
Это говорит следующее должно напечатать 1
Нет, это не говорит об этом.
Если кто-то хочет сослаться на переданный аргумент, ему нужно использовать std::ref, верно?
Да.
Но N3225 говорит, что std::bind(f, a1) должен возвращать упаковщик вызовов, который при вызове wrapper() будет вызывать INVOKE(f, v1), где v1 должен быть (аргумент, который я передал, другими словами, используя входящий параметр binds, который является идеальным параметром пересылки, std::forward(a1)).
Вот где ты не прав. "Связанные аргументы", которые вы передали при вызове bind, сохраняются в виде вновь созданных объектов типа TiD
каждый из которых построен из forward<Ti>(ti)
, Это становится достаточно понятным, говоря, что "tid - это значение типа TiD, построенное из std::forward<Ti>(ti)
". Благодаря специальной обработке эталонных оболочек, существует дополнительный" слой преобразования ". См. 20.8.10.1.2/10, в котором объясняется, как vi
а также Vi
относится к tid
а также TiD
,
В настоящее время он печатает 0, потому что в тот момент, когда вы вызываете std::bind, он не знает, что вы хотите передать ссылку. Он не смотрит на сигнатуру функции, чтобы увидеть, какие типы параметров она принимает, и соответственно корректировать.
Для правильной работы звоните
void f(int &a) { a = 1; }
int main() {
int a = 0;
std::bind(f, std::ref(a))();
std::cout << a << std::endl;
}
C++0x предлагает "идеальное связывание", но в этом есть огромная опасность, которая может серьезно нарушить существующий код, основанный на нынешнем поведении. Вот очень простой пример.
void myCallback( const std::string& str, int i );
function< void(int) > makeCallback( const std::string & str )
{
return bind( myCallback, str, _1 );
}
В настоящее время вы можете положиться на то, что bind скопирует строку, которую вы передаете str
и, следовательно, тот факт, что он будет действителен, будет вызван обратным вызовом.
Если бы он "умно" использовал "совершенную привязку" для хранения его в качестве ссылки, он бы сломал такие ситуации.