
Kevlin Henney wrote:
Something is still unclear. If the number of individual values a type can hold is the only criteria for "widening", then is T* -> shared_ptr<T*> conversion widening? After all, shared_ptr<T*> can hold all pointer values out there, plus it holds reference count, so the total number of values is greater.
The number of legal values is, in principle and in practice, smaller:
T o; boost::shared_ptr<T> p(&o); boost::shared_ptr<T> q(new T); // OK
You previous definition did not say anything about "legal values", and as far as bits are concerned, two pointers above are absolutely the same. It's the wider context -- such as memory allocator -- which distinguish between the validity of the values. And since that context is beyond type system, I'm still don't think you've given complete definition of "widening".
However, there is more to conversions than just narrower vs wider (see below).
If that's widening conversion, then constructor of shared_ptr<T*> need not be explicit, but that would be really unsafe. You say windening conversion often loose information, and in this fact you can easily loose information that pointer is allocated on stack, or that it's "this" pointer.
Information loss is not the deciding criterion for whether a conversion is narrowing or widening :-)
I mean to say that, according to your definition, this is narrowing conversion, but yet it looses important information outside type system, and that can lead to bad results.
Another distinction that needs to be made is that not all conversions fit into the widening/narrowing model. Some conversions are better considered as reinterpreting or translating conversions (eg lexical_cast and reinterpret_cast). These are useful where some notion of subtyping is not applicable, eg between int and string, as opposed to where one is, eg between numeric types.
Good, is T* -> shard_ptr<T*> conversion widening, or not? Or it does not fit into the model? - Volodya