Недавно я обнаружил, что shared_ptr
не имеет указателя на член оператора ->*
, Я создал простой пример:
template <typename Pointer, typename Function, typename... Args>
auto invoke1(Pointer p, Function f, Args... args) -> decltype((p->*f)(args...))
{
return (p->*f)(args...);
}
struct A {
void g() { std::cout << "A::g()\n"; }
};
int main() {
A a;
invoke1(&a, &A::g); // works!!
std::shared_ptr<A> sa = std::make_shared<A>();
invoke1(sa, &A::g); // compile error!!
}
Q1: почему так? Почему shared_ptr не имеет этого оператора?
Я добавил такой оператор для shared_ptr
и пример начал работать:
template <typename T, typename Result>
auto operator ->* (std::shared_ptr<T> pointer, Result (T::*function)()) ->decltype(std::bind(function, pointer))
{
return std::bind(function, pointer);
}
template <typename T, typename Result, typename Arg1>
auto operator ->* (std::shared_ptr<T> pointer, Result (T::*function)(Arg1 arg1)) ->decltype(std::bind(function, pointer, std::placeholders::_1))
{
return std::bind(function, pointer, std::placeholders::_1);
}
Q2: это правильная реализация для этого оператора? Есть ли где-нибудь какие-нибудь «золотые» правила, как реализовать такого оператора, возможно, я либо заново изобрел колесо, либо пошел в совершенно неверном направлении, как вы думаете? Есть ли способ иметь единственную функцию, реализующую этот оператор, вместо того, чтобы столько функций, сколько заполнителей в std …
После этого я пришел к выводу, что std::bind
может быть использован в моем invoke
метод.
template <typename Pointer, typename Function, typename... Args>
auto invoke2(Pointer p, Function f, Args... args)
-> decltype(std::bind(f, p, args...)())
{
return std::bind(f, p, args...)();
}
Таким образом, мой пример также работает без добавления operator ->*
в shared_ptr
,
Q3: Итак, это std::bind
теперь рассматривается как замена operator->*
?
В двух словах: да std :: bind является заменой указателей на функции-члены.
Зачем? потому что указатели на функции-члены ужасны, и их единственной целью является реализация делегатов, поэтому std :: bind и std :: function делают
Для справки о том, как реализованы указатели на функции-члены, смотрите мой предыдущий ответ Вот. Проще говоря, указатели на функции-члены ограничены стандартом, поскольку они не допускают вызовов после приведения; это делает их совершенно бессмысленными для такого поведения 90% людей хотят получить указатели на функции-члены: делегаты.
По этой причине std :: function используется для представления абстрактного «вызываемого» типа, а std :: bind используется для привязки этого к указателю на функцию-член. Вы абсолютно не должны связываться с указателями на функции-члены, а вместо этого использовать std :: bind и std :: function.
Я считаю, что самой простой мыслью было бы заменить «разыменование структуры» (->
) оператор с парой разыграний (*
) и структура ссылки (.
) операторы:
template <typename Pointer, typename Function, typename... Args>
auto invoke1(Pointer p, Function f, Args... args) -> decltype(((*p).*f)(args...))
{
return ((*p).*f)(args...);
}
я верю shared_ptr
не имеет оператора ->*
потому что это невозможно реализовать для произвольного числа аргументов (что C ++ 11 позволяет делать для других случаев использования). Кроме того, вы можете легко добавить перегрузку invoke
функция для умных указателей, которая вызывает get()
Так что усложнять интерфейс не желательно.