Передача указателя объекта в качестве первого аргумента для функции-члена: стандартно ли это?

Следующая программа компилируется как с gcc, так и с clang, но является ли это фактически стандартным С++ 11 или оба компилятора предпочитают поддерживать его для удобства?

struct Foo {
    int i;

    void bar() { std::cout << i << std::endl; }
};

int main() {
    std::function<void(Foo*)> method = &Foo::bar;

    Foo myFoo{4};
    method(&myFoo); // prints 4
}

Это, конечно, удобно, но я не понимаю, как это работает.

Ответы

Ответ 1

Да, это стандартно. [func.wrap.func.inv] указывает, что operator()(ArgTypes&&... args)
std::function вызывает

INVOKE (f, std::forward<ArgTypes>(args)..., R) (20.8.2), где f - целевой объект (20.8.1) *this.

(Где R - указанный тип возвращаемого значения.)

[func.require] определяет INVOKE:

Определите INVOKE (f, t1, t2, ..., tN) следующим образом:

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

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

  • [...]

Обратите внимание, что конечный R в вызове используется для преобразования в R (тип возврата function):

Определите INVOKE (f, t1, t2, ..., tN, R) как INVOKE (f, t1, t2, ..., tN), неявно преобразованный в R.

Первый и единственный аргумент, который вы указываете, является указателем на объект Foo -объект. Таким образом, вызов method вызывает вызов (void)((*t1).*f)(), который при написании с вашими значениями эквивалентен ((*(&myFoo)).&Foo::bar)(), что эквивалентно myFoo.bar().