Since the NonMemberFunction doesn't attempt to resize its argument, one would think there should be some way of passing the embedded Object straight from Derived::MemberFunction to the NonMemberFunction, via the latter's argument list.
O_o
You really can't blacklist public interfaces in the C++ language from being used. You can only pretend to blacklist public interfaces by dancing around a lot of techniques. The fact that no real way to blacklist public interfaces exists means that calling `NonMemberFunction` with the internal object is flawed because you can't actually know that `NonMemberFunction`, in general not the specific instance, doesn't use the interfaces the owner wouldn't want to be used.
That said, you could pretend to blacklist the relevant methods by implementing the proxy class `ObjectNoResize` in terms of `ObjectInterface` which overloads `resize` to always throw an exception. Of course, you now aren't trapping the misuse of the blacklisted member until program execution when you could be trapping the misuse during compilation by whitelisting only the methods required for the mechanisms being implemented.
I'd suggest you just whitelist only the interfaces you want to expose.
The problem there of course is that FixedSizeObject would then have to be the base class of Object, and it doesn't really make sense for the base class to have immutable dimensions, while the derived class can be resized.
The problem is that you are calling the parent class by the `FixedSizeObject` label thus lying in the interface.
You might think in terms of `ObjectResizable` which can be constrained as a `Object`, if you expose only the right interfaces, without lying.
Or perhaps an Object::Iterator would solve the problem?
I'd recommend using iterators anyway for what you seem to be doing, but I like iterators.
But of course point (4) demands that the embedded Object be passed by constant reference only. So I've fallen back on passing a local copy back and forth, which is a lousy kludge, and time consuming too.
I actually came to address the "kludge" comment.
The `MemberFunction` is a public method meaning anyone can use the method.
The `NonMemberFunction` utility used by `MemberFunction` doesn't throw an exception, but what if `NonMemberFunction` did throw an exception?
The current implementation of `MemberFunction` makes any exception thrown by `NonMemberFunction` irrelevant because the internal state isn't modified until `MemberFunction` successfully completes. The `MemberFunction` implementation then makes the strong exception guarantee. If you were to directly provide access to the internal state, the `MemberFunction` implementation would loose the strong guarantee. A primitive which implements the strong guarantee is extremely useful because clients using the interface can thus implement the strong guarantee without wrapping the used interfaces in otherwise pointless boilerplate.
In other words, because you are implementing the strong guarantee I as client do not have to write my code to prevent mutating the state in the face of an exception. I don't have to do this:
Code:
void MyFunction(const Derived & f, /* ... */)
{
Derived sCopy(f);
// use sCopy
f = sCopy;
}
The usefulness of making a copy isn't just limited to the potential of exceptions. What if `NonMemberFunction` was written by a third-party which eventually did change the size of the collection? By making a copy, you can enforce the statement "The `MemberFunction` function doesn't change the size of the collection." by throwing an exception if the size is unexpectedly different instead of replacing the internal element.
*sigh*
Now, we can talk about performance. I, again, am all for making the copy. We don't have to make the copy twice to still hit all the important notes.
We are looking at copying an `Object`, and we are responsible for implementing the `Object` class.
What is the `Object` class? Maybe a pointer to `int` for the data and a `int` for the size? Okay. We need to implement `swap` which will swap, obviously, the bits of two `Object` instances such that the exchange can't throw an exception. Because we only have a pointer and an integer, we can manage the exception guarantee with ease.
Code:
void Object::swap
(
Object & fOther
)
{
using std::swap; // important
swap(this->mDataPointer, fOther.mDataPointer);
swap(this->mDataSize, fOther.mDataSize);
}
We need to implement `Base::replace` in terms of the new method.
Code:
void Base::replace
(
Object & f
)
{
embedded.swap(f);
}
The implementation for `MemberFunction` hasn't changed, but we have reduced the cost.
We could do something similar even if we didn't control the implementation for the `Object` class.
Now, you will notice that `NonMemberFunction` has some preconditions which are checked before anything is done with the argument. Unfortunately, we will copy the `Object` instance even if the preconditions aren't matched. We can avoid the copy by implementing the preconditions as a state of the `NonMemberFunction` function allowing the implementation for `MemberFunction` to check if `NonMemberFunction` would do anything before making the copy.
*shrug*
I do not see making the copy before passing the state to an external function as a kludge. I see making the copy as being a part of a correct and robust implementation. You'll be able to enforce any relevant guarantees of the `Derived` class interface before any of the operations might have taken place.
Soma