To break or not to break… encapsulation

“The devil is in the details” 

A particularity of the C++ data validation concept I wrote about is passing that wrapped_value object as an argument to a function. The reason is for that wrapped value to behave like the type it wraps so that it has a natural usage. I should not know the actual value is hidden by a level of indirection, making it easy to control any mutation.

To achieve that feature, I have used the user-defined conversion function and it went smooth. I can have a wrapped value and pass it as an argument (by value or const reference) to a function:

int inc_by_value(int v) { return v + 1; }
int inc_by_const_ref(const int& a) { return a + 1; }

msd::wrapped_value<int, UpperBoundLimiter<int, 10>> value = 2;
assert(inc_by_value(value) == 3);
assert(inc_by_const_ref(value) == 3);

Pass by non-const reference

The user-defined conversion I initially implemented is the const reference overload:

operator const Value&() const noexcept { return value_; }

That’s why I can pass the wrapped value by value and const reference. To pass it as a non-const reference, I have to implement the non-const reference overload:

template <typename Value, typename... Wrappers>
class wrapped_value {
    // ...
    
    operator Value&() noexcept { return value_; }

    // ...
}

And I can pass by reference and mutate the value:

void inc_by_ref(int& a) { ++a; }

msd::wrapped_value<int, UpperBoundLimiter<int, 10>> value = 2;
inc_by_ref(value);
assert(value == 3);

or

void update(int& a) { a = 20; }

update(value);
assert(value == 10); // should be 10 because of the UpperBoundLimiter

But the last assertion fails: Assertion `value == 10′ failed. Continue reading To break or not to break… encapsulation