This approach does not allow me:
- return by value in the const member function (because I want the compiler to catch the assignment to objects const ca.value () = 2).
I do not understand what do you mean. If you mean what, I think, you mean, you will be pleasantly surprised :) Just try to return the constant member by value and see if you can do ca.value()=2 ...
But my main question is if you want some kind of input check, why not use a dedicated setter and a dedicated getter
struct A { int value() const { return value_; }
It will even reduce the amount of input! (one click '='). The only drawback to this is that you cannot pass a value by reference to a function that modifies it.
As for your second example after editing, with vector - using your receiver / setter makes even more sense than your original example, because you want to provide access to the values (allow the user to change the values), but NOT to the vector (you don’t want to, so that the user can resize the vector).
So, although in the first example I would recommend making a public participant, in the second it is clearly not an option, and using this form of getters / setters is really a good option if input verification is not required.
Also, when I have classes like your second type (with a vector), I like to give access to begin and end iterators. This allows for more flexible use of data with standard tools (while not allowing the user to resize vector and allowing you to easily change the type of container)
Another advantage of this is that random access iterators have operator[] (like pointers), so you can do
vector<int>::iterator A::value_begin() {return values_.begin();} vector<int>::const_iterator A::value_begin()const{return values_.begin();} ... a.value_begin()[252]=3; int b=a.value_begin()[4]; vector<int> c(a.value_begin(),a.value_end())
(although it can be pretty ugly that you still want your getters / setters in addition to this)