`operator->*` для типов стандартной библиотеки, имеющих `operator->`
pavelkryukov opened this issue · 0 comments
pavelkryukov commented
Предложение
Определить operator->*
для того, что в стандартной библиотеке умеет в operator->
.
Навскидку вспоминаю unique_ptr
, shared_ptr
, optional
, итераторы.
template<typename R>
R& operator->*(R T::*member) const
{
return this->operator->()->*member;
}
template<typename R, typename ... Args>
auto operator->*(R (T::*member)(Args...)) const
{
return [ptr=this->operator->(), member](Args&& ... args) { return (ptr->*member)(std::forward(args)...); };
}
template<typename R, typename ... Args>
auto operator->*(R (T::*member)(Args...) const) const
{
return [ptr=this->operator->(), member](Args&& ... args) { return (ptr->*member)(std::forward(args)...); };
}
Как я догадываюсь, это не было сделано изначально из-за отсутствия лямбд и вариабельных шаблонов, без них решение весьма громоздко [1].
Область применения
Если к разным полям хранимой по smart-pointer/итераторам структуры применяются одни и те же алгоритмы, то хочется убрать повторяющийся код и использовать одну (мета-)функцию, принимающую pointer-to-member
аргументом.
Схожий пример с методами – мультиплицирующий адаптер [2]:
class Interface
{
public:
virtual void foo(int x) = 0;
virtual void bar(int y, int z) = 0;
// много других функций
};
class Adapter : public Interface
{
std::list<std::shared_ptr<Interface>> children;
public:
void foo(int x) override
{
for (auto& e : children)
e->foo(x);
}
void bar(int y, int z) override
{
for (auto& e : children)
e->bar(y, z);
}
// и так далее...
};
// то же, но немного проще:
class Adapter2 : public Interface
{
std::list<std::shared_ptr<Interface>> children;
template<auto f, typename ... Args>
void adapt(Args&& ... args)
{
for (auto& e : children) {
//(e->*f)(std::forward<Args>(args)...); // ошибка компиляции!
(*e.*f)(std::forward<Args>(args)...); // а это работает
}
}
public:
void foo(int x) override { adapt<&Interface::foo>(x); }
void bar(int y, int z) override { adapt<&Interface::bar>(y, z); }
// и так далее, но уже в два раза меньше кода
};
Прожить с (*e.*f)
вместо (e->*f)
можно, но хотелось бы полной симметрии с C-указателями.
Ссылки
- Имплементация такой перегрузки в C++98 от Scott Meyers: https://www.aristeia.com/Papers/DDJ_Oct_1999.pdf
- Пример для
unique_ptr
: https://godbolt.org/z/oY6joezca