Ответ 1
std::reference_wrapper::operator()
выполняет несколько "магии" в некоторых случаях помимо того, что вызовет прямая функция. Его эффекты указаны как (цитирование N4296 [refwrap.invoke]):
template <class... ArgTypes> result_of_t<T&(ArgTypes&&... )> operator()(ArgTypes&&... args) const;
Возвращает:
INVOKE(get(), std::forward<ArgTypes>(args)...)
. (20.9.2)
где get()
возвращает ссылку на то, что обертывает reference_wrapper
. INVOKE
описывается в 20.9.2 [func.require]:
Определите
INVOKE(f, t1, t2, ..., tN)
следующим образом:(1.1) -
(t1.*f)(t2, ..., tN)
, когдаf
является указателем на функцию-член классаT
, аt1
является объектом типаT
или ссылкой на объект типаT
или ссылку на объект типа, полученного изT
;(1.2) -
((*t1).*f)(t2, ..., tN)
, когдаf
является указателем на функцию-член классаT
иt1
не является одним из типов, описанных в предыдущем пункте;(1.3) -
t1.*f
, когдаN == 1
иf
является указателем на данные элемента классаT
иt1
является объектом типаT
или ссылкой на объект типаT
или ссылка на объект типа, полученного изT
;(1.4) -
(*t1).*f
, когдаN == 1
иf
является указателем на данные элемента классаT
иt1
не является одним из типов, описанных в предыдущем пункте;(1.5) -
f(t1, t2, ..., tN)
во всех остальных случаях.
Результат вызова ref(f)
вместо простого f
заключается в том, что функция-указатель-член-член и данные-указатели на элемент могут быть "вызваны" с соответствующим указателем/ссылкой объекта в качестве параметра. Например,
struct A { void foo(); };
struct B : A {};
struct C : B {};
for_each_arg(&A::foo, A{}, B{}, C{}, std::make_unique<A>());
будет вызывать foo
в A
, B
и C
временных объектах и объекте, хранящемся в unique_ptr
(DEMO). Почему один предпочитает использовать ref(f)
над f
, очевидно, будет зависеть от контекста, в котором используется for_each_arg
.