14

To clarify a possible precedence ambiguity in the English language: We are taking about "smart (pointer to member)", not "(smart pointer) to member".

I would define a smart pointer to member as a class X with operator ->* (T* lhs, X rhs) overloaded.

In his article "Implementing operator->* for Smart Pointers", Scott Meyers only briefly touches smart pointer to member, because back then (1999) the specific problem was difficult enough for raw pointer to member (side note: the latter is solved elegantly with lambdas here).

Anyhow, Scott Meyers writes in a footnote:

Shortly after writing the draft of this article, one of my consulting clients showed me a problem that was naturally solved by smart pointers to members. I was surprised, too.

I've tried to find an example for such a natural smart pointer to member. But I could neither come up with anything myself nor did online search uncover what I was looking for.

Do you know of any real world "smart pointer to member" example?

EDIT: I'm not looking for any ->* overload (as done by some EDSLs). Aiming at examples with semantics that resemble the built-in ->*, my definition above explicitly requires lhs to be a raw pointer.

Tobi
  • 2,591
  • 15
  • 34
  • Good call with the disambiguating preamble. – Quentin Feb 07 '18 at 13:28
  • cppreference mentions that it's ".. in fact is used in that capacity by actors in boost.phoenix. It is more common in EDSLs such as cpp.react." – Mihayl Feb 07 '18 at 13:30
  • 1
    If we only could ask his consulting client ;) –  Feb 07 '18 at 13:30
  • Even here on SO is an interesting example https://stackoverflow.com/q/23619152/8918119. But none of them is really as smart pointer – Mihayl Feb 07 '18 at 13:35
  • In the cpp.react example `->*` is not used as *access through pointer to member*. They just used it in their EDSL and could have picked any other binary operator. It does not match my definition as `lhs` is not a raw pointer. – Tobi Feb 07 '18 at 13:41
  • https://stackoverflow.com/q/2696864/8918119 – Mihayl Feb 07 '18 at 13:47
  • Somehow related: https://stackoverflow.com/questions/17696664/about-shared-ptr-and-pointer-to-member-operator-and-stdbind – PiotrNycz Feb 27 '18 at 00:13

1 Answers1

1

You have a layout engine that should work with 90 degree rotation (height and width swapping).

Using smart member pointers you can make (foo->*x) and (foo->*y) swap meaning. The same smart x and y can work on different unrelated kinds of data (rectangles, points, vectors), even on types you don't own.

template<class F>
struct smart_pm_t {
  F f;
  template<class T>
  friend decltype(auto) operator->*(T* t, smart_pm_t const& self ) {
    return self.f(t);
  }
};

template<class F>
smart_pm_t<F> smart_pm( F f ) { return {std::move(f)}; }

auto x = smart_pm( [](auto* t)->decltype(auto) noexcept { return t->x; } );
auto y = smart_pm( [](auto* t)->decltype(auto) noexcept { return t->y; } );

and we can get fancier with them.

do_layout( data, x, y );

vs

do_layout( data, y, x );

solves the problem horizontally or vertically.

This is an actual (simplified) use case in actual production code that reduced code duplication and ironed out a whole pile of bugs, because we only had to fix the code once.

A right hand normal member pointer had the problem that we would have to pass an entire suite of member pointers, one per type that works on the left hand side.

In effect, we wrote a compile-time polymorphic member pointer.

Tobi
  • 2,591
  • 15
  • 34
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Thanks for the insightful example @Yakk. Maybe I am missing something, but that looks to me like syntactic noise around a lambda call: As `do_layout` has to be a template anyway you could just do `auto get_x = [](auto* t)->decltype(auto) { return t->x; };` (note that the lambda is the same as yours), call `do_layout(data, get_x, get_y)` with `get_x(data)` instead of `data->*x` inside and thus more easily achieve the same thing. Or is it possible to motivate the use of `->*` by expanding the example (you mention that it is simplified)? – Tobi Feb 08 '18 at 11:12
  • What is the motivation for overloading `->*` instead of just passing the accessor as a lambda? Would be really great if you could explain that! @Yakk – Tobi Feb 16 '18 at 09:20
  • @tobi I am uncertain what you are asking. Yes, every line of code can be rewritten differently. `a+b` can always be rewritten as `f(a, b)` for any operator `+` or `f(b)(a)`. In this case `x` behaves as a member pointer, using member pointer syntax makes sense. – Yakk - Adam Nevraumont Feb 16 '18 at 13:16
  • overloading `+` is common, but `->*` is rarely used (even without overloading). So what's your story for writing the extra code? Historical (you used to have *raw* mem ptrs and refactored)? Same algorithms used both with *raw* and *smart* member ptrs? I'm just curious... – Tobi Feb 19 '18 at 10:14