Какого черта делает std :: bind (x, y)? - PullRequest
13 голосов
/ 21 февраля 2011

Я застрял, читая описание 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 ' s входящий параметр, который является идеальным параметром пересылки, std::forward<A1>(a1)).

INVOKE(f, a) определяется от 20.8.2 до f(a). Таким образом, это определяет, что вызов возвращаемой оболочки вызовов передает исходный аргумент . Чего мне не хватает?

Ответы [ 3 ]

5 голосов
/ 21 февраля 2011

Это говорит, что следующее должно напечатать 1

Нет, это не говорит, что.

Если кто-то хочет сослаться на переданный аргумент, одиннужно использовать std :: ref, верно?

Да.

Но N3225 говорит, что std :: bind (f, a1) должен возвращать оболочку вызова, которая привызов функции wrapper () вызовет INVOKE (f, v1), где v1 должен быть (аргумент, который я передал, другими словами, используя входящий параметр binds, который является идеальным параметром пересылки, std :: forward (a1)).

Вот где вы не правы.«Связанные аргументы», которые вы передали при вызове связывания, хранятся в виде вновь созданных объектов типа TiD, каждый из которых построен из forward<Ti>(ti).Это можно сделать достаточно ясно, сказав, что «tid - это значение типа TiD , построенное из std::forward<Ti>(ti)».Благодаря специальной обработке эталонных оболочек, существует дополнительный «слой трансформации».См. 20.8.10.1.2 / 10, в котором объясняется, как vi и Vi относятся к tid и TiD.

3 голосов
/ 21 февраля 2011

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

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

2 голосов
/ 21 февраля 2011

Ух ты, это запутанно, невероятно.Он определяет 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 ссылаться на него.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...