I've found a few threads that heavily imply this can't be done, but none use exactly the same combination of operators and conditions, so I'd like to ask more specifically. Hopefully that means it's a quick and easy answer for someone... one way or another!
Consider an example proxy class, made to manage a value within a larger block of storage - as in this oversimplified but representative example:
class SomeProxyThing {
std::uint32_t storage;
public:
operator std::uint16_t() const
{
return storage & 0x0000FFFF;
}
SomeProxyThing &operator=(std::uint16_t const value)
{
storage &= 0xFFFF0000;
storage |= value;
}
};
I want all assignments to work via the user-defined operator
s. The user should only be able to pass in or get out the 'exposed' type, in this case std::uint16_t
. I might be using various proxy class types and want this to apply to all of them. Ideally, for any combination of types, I could just type someProxy = anotherProxy
and let the compiler do the rest.
But when the left- and right-hand-side of the assignment have the same or inheritance-related types, the default copy assignment operator - of course - conflicts with this goal. It copies the entire storage
, thus clobbering the other half of that uint32_t
- rather than copying just the 'exposed' value as desired. And rightly so! For most cases. But I'd like a way to 'assign by conversion' even if LHS and RHS types are the same. To avoid this, I can:
- redefine the copy assignment operator to perform a 'proxied' copy using the user-defined
operator
s - which is what I've been doing, but it seems kinda hacky and, like any user-defined constructor/assignment operator, breaks the trivially copyable status of thestruct
- which I need to keep. It stillmemcpy()
s anyway ing++
, but I want defined behaviour. - or
= delete
the copy-assignment operator (which we can now do for TC types). But assignments still try to use it and throw a compile error - sincedelete
means 'abort with an error if I'm the chosen overload', not 'exclude me from overload resolution'. To get around this, I must explicitly tell the compiler to use the conversion operator and assign from its result:
SomeProxyThing a, b;
a = 42;
b = static_cast<std::uint16_t>(a);
// a.k.a.
b.operator=( a.operator std::uint16_t() );
There doesn't seem to be a way to tell the compiler 'ignore any error generated by your preferred overload and pick the next best one'. Is there? More generally, is there any way/hack/horrifying kludge, in such a situation, to force the compiler to automatically use/prefer certain operator
s?
In other words, ideally, in
SomeProxyThing a, b;
a = 42;
b = a;
that b = a;
would really do this:
b = static_cast<std::uint16_t>(a);
// a.k.a.
b.operator=( a.operator std::uint16_t() );
without me having to type this manually, use a static_cast
, or implement named get/set methods. Ideally, I want reads/writes to any such proxy to look exactly like reads/writes to basic types in written code, all using =
.
I strongly suspect that's not possible... but confirmation would be nice!