Какая черта делает 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)
. Таким образом, это определяет, что вызов обертки возвращенного вызова передает исходный аргумент. Что мне не хватает?
Ответы
Ответ 1
Ничего себе, это путает неверие. Он определяет v1
как tid
, и он как следующий (ti
является i-м совершенным параметром связывания пересылки, а tid
является затухающим типом этого параметра - т.е. массив становится указателем и т.д.).
tid
- это значение типа tid
, построенное из std::forward<Ti>(ti)
Хорошо, я сказал, что это tid
std::forward<Ti>(ti)
, и это lvalue! Но это не то, что на самом деле означает сказать. Это означает
tid
является lvalue типа tid
, который ссылается на объект, построенный из std::forward<Ti>(ti)
Теперь это имеет гораздо больше смысла. Потому что, если std::forward<Ti>(ti)
на самом деле rvalue? "Lvalue... built from..." означает, что мы создаем новый объект из "..." и ссылаемся на значение lvalue.
Ответ 2
В нем говорится следующее: напечатать 1
Нет, он этого не говорит.
Если вы хотите ссылаться на переданный аргумент, нужно использовать std:: ref, right?
Да.
Но N3225 говорит, что std:: bind (f, a1) возвращает оболочку вызова, которая при вызове wrapper() вызовет INVOKE (f, v1), где v1 должен быть (аргумент, который я передал, в другие слова, используя привязывающий входящий параметр, который является идеальным параметром пересылки, std:: forward (a1)).
Что вы ошибаетесь. "Связанные аргументы", которые вы передали при вызове bind, сохраняются в виде вновь созданных объектов типа TiD
, каждый из которых построен из forward<Ti>(ti)
. Это сделано достаточно убедительно, говоря: "tid - это значение типа TiD, построенное из std::forward<Ti>(ti)
". Из-за специальной обработки ссылочных оберток имеется дополнительный "слой преобразования". См. 20.8.10.1.2/10, в котором объясняется, как vi
и vi
относятся к TiD
и TiD
.
Ответ 3
В настоящее время он печатает 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;
}
С++ 0x предлагает "идеальное связывание", но с этим существует огромная опасность, которая может сильно сломать существующий код, который опирается на настоящее поведение. Вот очень простой пример.
void myCallback( const std::string& str, int i );
function< void(int) > makeCallback( const std::string & str )
{
return bind( myCallback, str, _1 );
}
В настоящее время вы можете полагаться на bind, копируя строку, которую вы передаете с помощью str
, и, следовательно, факт, что она будет действительной, будет вызвана вызовом обратного вызова.
Если он "умно" использовал "идеальное связывание", чтобы сохранить его в качестве ссылки, он нарушил бы подобные ситуации.