
From: Stjepan Rajko
Ah, I see. You focused on the typically mutating operators because there you can reasonably assume that the return type should be the constrained object. This would be good to add to the docs, if it's not already there.
To be done.
OK, that makes sense. The policy is the one place that guarantees to leave the object in a valid constrained state, so it is the one place that is allowed to directly change the constraint. This would also be good to mention or reference when you talk about change_constraint (since as a user of the constrained object, I could be providing the policy myself).
To be done.
Sorry, I meant `mutable` as in the mutable keyword. For example:
struct observable_int { // initialization omitted
int observe() const { m_times_observed++; return m_value; }
unsigned times_observed() const { return m_times_observed; }
private: int m_value; mutable unsigned m_times_observed; // initialized to 0 }
// One could think that this would be a reasonable constraint struct is_viewed_few_times { bool operator () (const observable_int &x) const { return x.times_observed()<10; } };
constrained<observable_int, is_viewed_few_times> x;
// but it is not enforced for(int i=0; i<20; i++) x.value().observe(); // never complains
You're right, this is not a valid usage of constrained. The rule is that the result of the constraint invocation for the value must always be identical as long as you don't access either of them as non-const. Here the value and the constraint are accessed as const only, yet the result of the constraint invocation may be different for two subsequent invocations. I guess this is another thing that should be stated explicitly in the docs? ;-)
Speaking of access to the underlying object in situations where you need non-const access to it... you could provide a member function that takes a unary Callable as a parameter, and calls the Callable with a copy of the underlying object as the argument. After the call returns, it assigns the (perhaps modified) value of the copy back to the underlying object (through the policy / checking the constraint). AFAICT, your guarantee is still never violated, and this would provide a really useful piece of functionality.
Maybe I've missed something, but this is not too different from what you can already do (having constrained x and callable f): // copy the value, modify, assign x = f(x.value()); The difference is that here f is responsible for making the copy. Are there some other important factors that would justify adding the member you describe?
Instead of using a copy you could also use the underlying object as the argument directly, but that weakens your guarantee (and if the Callable keeps an address of the object, throws the guarantee out the window).
No, we don't want this. ;-) Best regards, Robert