I implemented a container template class owning a storage of type std::unique_ptr
with customizable deleter, as follows:
template <class D>
struct Container
{
Container(const char* str)
: data(typename D:: template make(str))
{ }
std::unique_ptr<const char[], D> data;
};
Here's what the deleter (container template parameter) might look like:
struct DefaultDelete : std::default_delete<const char[]>
{
static const char* make(const char* str)
{
auto p = new char[strlen(str) + 1];
memcpy(p, str, strlen(str) + 1);
return p;
}
};
struct CustomDelete
{
static const char* make(const char* str)
{
// perform custom allocation & construction...
auto p = str; // simplified example
return p;
}
void operator()(const char* p) const noexcept
{
// perform custom deletion...
}
};
Now, I want that an object of type Container<CustomDelete>
can be implicitly casted as const Container<DefaultDelete>&
.
In order to do so, I implemented the following type-cast operator:
template <class D>
struct Container
{
... (same as above)
template <class E>
operator const Container<E>& () // implicit type-cast
{
return reinterpret_cast<const Container<E>&>(*this);
}
};
Tested on Linux/gcc and Windows/msvc, this works as expected:
void print(const Container<DefaultDelete>& c)
{
std::cout << c.data.get() << "\n";
}
int main()
{
const char* const source = "hello world";
Container<DefaultDelete> cd(source);
print(cd);
Container<CustomDelete> cc(source);
print(cc);
return 0;
}
results in:
hello word
hello word
However, as far as I understand, the above implementation of the type-cast operator violates strict aliasing rules and, although test worked as expected, it leads to undefined behavior.
So, my questions are:
Can the type-cast operator be implemented in such a way as not to violate strict aliasing rules ? And what is such implementation ?
What I want to achieve is to be able to pass a Container<CustomDelete>
object to any function that needs a const Container<DefaultDelete>&
(just like the print()
function above) without the need to convert/create a new Container<DefaultDelete>
object, because heap allocation is not allowed in the context where I have to call the function.
I noticed that when the CustomDelete
class has a different size from DefaultDelete
size, then reference returned by type-cast operator is broken.
To compensate for this, I added a static assertion in operator implementation to check that both types have the same size, ie:
static_assert(sizeof(Container<D>) == sizeof(Container<E>), "size mismatch");
Assuming any positive answer to my 1st question exists, what other tests should be performed to make sure the type-cast works properly ?
Can the type-cast operator be implemented in such a way as not to violate strict aliasing rules ?
No. It's not the cast itself that's the problem. It's that accessing the result of the cast, no matter how it was done, violates strict aliasing.