I think the lifetimes of the (sub)objects of type A do need to end. What needs to not end is their enclosing objects. In the example I provided, the lifetime of the array object owned by the vector must not end (note that the converted elements might not include all elements of the array). Similarly, if the array were a data member of a class type C, the in-place-converting-cast thing must not end the lifetime of the enclosing object of type C.Thanks, Tom. I'd like to make the following observations:The goal is to temporarily replace objects of type A with objects of type B while preserving the object representation, then accessing/modifying the replacement objects using only type B, then replace the objects of type B with objects of type A while again preserving the object representation in such a way that pointers/references to the original set of objects transparently refer to the new set of objects. Key to making this work is specifying that these operations do not end the lifetime of any enclosing objects.It seems that it would be a big change to the abstract machine if starting the lifetime of objects of type B did not end the lifetime of objects of type A (which formerly occupied the same storage).
Agreed, I forgot that implicit conversions are specified for these types.std::inplace_converting_array_cast<std::float64_t>(x.data(), x.size()); std::inplace_converting_array_cast<std::float64_t>(y.data(), y.size()); daxpy(n, std::bitcast<std::float64_t>(alpha), x.data(), y.data()); std::inplace_converting_array_cast<double>(y.data(), y.size()); std::inplace_converting_array_cast<double>(x.data(), x.size());A minor point is that std::bitcast<std::float64_t>(alpha) is not needed. One can just write alpha and implicit conversion will work.
Yes, absolutely. I had intended to write exactly what you did below, but got distracted thinking about the general problem and forgot to add the pointers. Sorry about that.However, x.data() is still double*, so in the daxpy call one would need reinterpret_cast<std::float64_t*>(x.data()), and similarly for y.data(). So, std::inplace_converting_array_cast<std::float64_t> could, for convenience, return a std::float64_t*.
Exactly.std::float64_t* x_ptr = std::inplace_converting_array_cast<std::float64_t>(x.data(), x.size()); std::float64_t* y_ptr = std::inplace_converting_array_cast<std::float64_t>(y.data(), y.size()); daxpy(n, alpha, x_ptr, y_ptr); std::inplace_converting_array_cast<double>(y_ptr, y.size()); std::inplace_converting_array_cast<double>(x_ptr, x.size()); Though, it seems like a fundamental change that std::inplace_converting_array_cast() has the effect of laundering all pointers that point to a given address. I think that's what it does--after all the code above, the vectors' pointers to double are again valid.
If that direction seems promising, it might be worth brainstorming on something more general. Maybe, std::launder_all_pointers(void *, std::size_t)
You need something that TBAA can be made aware of so that the compiler knows when it is necessary to perform reloads. The actual pointer value and number of elements aren't particularly important for that; they are more useful for sanitizers or for constant evaluation (if these operations were to be permitted during constant evaluation where UB has to be detected).
For a more blunt instrument, see SG16 #67
(Alias barriers; a replacement for the ICU hack).
This is the only approach I've been able to come up with that I think has a chance of working with the existing memory and object models while preserving TBAA and without being overly structural. It would be a very sharp edged tool.Personally, I don't see this as promising and don't intend to propose it, but it is interesting, and I appreciate your time and effort.
It's worth noting that GCC, for example, has the flag -fno-strict-aliasing Possibly compilers could offer something more restrictive, such as -fno-fp-strict-aliasing That may be a practical solution, but then the code isn't really C++ any more; it's a custom dialect.
Compilers could offer such options and the actual changes that would be needed for GCC and Clang are small. But yes, the options would only be useful for writing non-portable code.
Tom.