Какая черта делает 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, и, следовательно, факт, что она будет действительной, будет вызвана вызовом обратного вызова.

Если он "умно" использовал "идеальное связывание", чтобы сохранить его в качестве ссылки, он нарушил бы подобные ситуации.