Encapsulation preserving operator= overloading in C++ I have made a facility to perform operator= dispatching to a member setter function. The purpose is to enable accessing members (like using object.member = value;) while at the same time respecting encapsulation.

My implementation looks like this:

#include &lt;iostream&gt;

#define OPEQ_SETTER_GETTER( OwnerType, Type, Function, Name ) \
    accessor&lt; Type, OwnerType, decltype(&amp;OwnerType::Function) &gt; Name = \
    accessor&lt; Type, OwnerType, decltype(&amp;OwnerType::Function) &gt;(this, &amp;OwnerType::Function); \
    friend class accessor&lt; Type, OwnerType, decltype(&amp;OwnerType::Function) &gt;;

// A generic accesor class
template &lt;typename T, class Owner, class Setter&gt; struct accessor {
    accessor ( Owner* o, Setter &amp;&amp;f ): owner( o ), setter( f ) { }
    accessor &amp;operator=( const T &amp;value ) { setter( owner, data, value ); return *this; }
    operator T() const { return data; }
private:
    Owner *owner;
    Setter setter;
    T      data;
};

// An example of applying the idiom
class example
{
    private:
    static void setx( example *this_handle, double &amp;data, const double &amp;value )
    {
        std::cout &lt;&lt; "Setting data to: " &lt;&lt; value &lt;&lt; '\n';
        this_handle-&gt;another_member = 85656;
        std::cout &lt;&lt; "another_member is: " &lt;&lt; this_handle-&gt;another_member &lt;&lt; '\n';
        data = value;
    }

    std::size_t another_member = 0;

    public:
    example( )  {}

    OPEQ_SETTER_GETTER( example, double, setx, x );
    // Will expand to:
    //accessor&lt; double, example, decltype(&amp;example::setx) &gt; x = 
    //      accessor&lt; double, example, decltype(&amp;example::setx) &gt;(this, &amp;example::setx);
    //friend class accessor&lt; double, example, decltype(&amp;example::setx) &gt;;

};

int main()
{
    example e;
    e.x = 2*3.14159;
    std::cout &lt;&lt; "New value: " &lt;&lt; e.x &lt;&lt; '\n';

    return 0;
}


Demo

The questions I have questions are:


Does anybody see any issues with the above?
Does anybody have any suggestions in implementing this without using the preprocessor and still keep the interface similarly compact?
Design
I think it obfuscates the code with no real advantage.
// This adds an object `x` to the class `example` that
// when assigned a double will forward the
// value to the private method `setx()`
OPEQ_SETTER_GETTER( example, double, setx, x );

I think it would be easier to add a public function that forwards the value.
public:
void publicSetX(double v) {setx(v);}  // ? easier to read.

Also I add my usual rant about getters/setters breaking encapsulation by exposing internal implementation details.
Code Review
Make sure this is noted as a non-owning pointer.
Owner *owner;

When you construct the object. Owner should never be nullptr
accessor ( Owner* o, Setter &amp;&amp;f ): owner( o ), setter( f ) { }

So pass owner by reference to indicate this. Its OK to store as a pointer (as its hard to copy objects with references without additional work). But your interface should be as clean as possible so there is no misunderstanding. Pass by reference to indicate it can't be nullptr and that this object is NOT taking ownership.
The setter value f can be bound to an r-value reference. But the parameter f will not pass it on as an r-value reference. Named objects can not be r-value references. So you need to use std::forward to activate perfect forwarding. Otherwise you are just activating the copy constructor (not the move constructor).
accessor ( Owner&amp; o, Setter &amp;&amp;f ): owner( &amp;o ), setter( std::forward&lt;Setter&gt;(f) ) { }

OK. For doubles passing a reference around is fine. But this is supposed to be generic. You also need to allow for move assignment.
// What if T is big.
// I would want to move the `value` into the object using assignment.
accessor &amp;operator=( const T &amp;value ) { setter( owner, data, value ); return *this; }

Reading the value generates a copy. This is not usually desirable. Especially if T is large.
operator T() const { return data; }

I would do:
operator T const&amp;() const { return data; }