Используя std::bind с функцией-членом, использовать указатель объекта или нет для этого аргумента?

Когда используешь std::bind чтобы связать функцию-член, первым аргументом являются объекты this указатель. Однако он работает, передавая объект как указатель, так и нет.

Смотрите, например, следующую программу:

#include <iostream>
#include <functional>

struct foo
{
    void bar(int v) { std::cout << "foo::bar - " << v << '\n'; }
};

int main()
{
    foo my_foo;

    auto f1 = std::bind(&foo::bar, my_foo, 1);
    auto f2 = std::bind(&foo::bar, &my_foo, 2);

    f1();
    f2();
}

И clang, и GCC компилируют это без жалоб, и результат работает для обоих связей:

foo:: bar - 1
foo:: bar - 2

Я пытался обернуть голову вокруг спецификации (раздел 20.8.9), но это одно из тех мест, где оно мне далеко не ясно.

Должен ли быть верным только один или оба?

3 ответа

Решение

Оба верны. 20.8.9.1.2 переадресация на 20.8.2 для описания требований и последствий вашего звонка bind, 20.8.2 это:

20.8.2 Требования [func.require]

1 Определите INVOKE(f, t1, t2, ..., tN) следующее:

- (t1.*f)(t2, ..., tN) когда f указатель на функцию-член класса T а также t1 является объектом типа T или ссылка на объект типа T или ссылка на объект типа, производного от T;

- ((*t1).*f)(t2, ..., tN) когда f указатель на функцию-член класса T а также t1 не является одним из типов, описанных в предыдущем пункте;

- t1.*f когда N == 1 а также f указатель на данные члена класса T а также t1 является объектом типа T или ссылка на объект типа T или ссылка на объект типа, производного от T;

- (*t1).*f когда N == 1 а также f указатель на данные члена класса T а также t1 не является одним из типов, описанных в предыдущем пункте;

- f(t1, t2, ..., tN) во всех остальных случаях.

Первые два параметра позволяют использовать как ссылку, так и указатель.

Здесь важно отметить, что формулировка не ограничивает вас простыми указателями. Вы могли бы использовать std::shared_ptr или какой-то другой умный указатель, чтобы сохранить ваш экземпляр живым, пока он связан, и он все равно будет работать с std::bind как t1 разыменовывается, независимо от того, что это (учитывая, конечно, что это возможно).

Добавить к правильному ответу (что разрешены обе формы).

Я думаю о двух вариантах привязки по аналогии с объявлением аргумента функции, которое может быть "передано по значению" или "передано по ссылке".

В случае f1 (иначе прохождение my_foo "по значению") результат не "видит" никаких изменений, внесенных в my_foo мимо обязательной точки. Это может быть нежелательно, особенно если my_foo эволюционирует. Привязка "По значению" имеет дополнительную "стоимость" (нескольких) обращений к конструктору копирования.

Есть разница. Как указал Ритис, передача по значению не приводит к изменениям, внесенным в my_foo. Например, в случае, когда my_foo является классом, передача по значению не приводит к изменению данных, внесенных в данные члена my_foo.

Другие вопросы по тегам