Re: boost::any: explicit constructor?

In message <uk727uuvv.fsf@boost-consulting.com>, David Abrahams <dave@boost-consulting.com> writes
The conversion to boost::any isn't a widening conversion in the usual sense, though, is it? Normally, a widening conversion preserves not only the information but the *interface* of the original data -- you wouldn't consider an implicit conversion from int to string a widening conversion, would you?
Your assumption is not quite correct: most widening conversions are not interface preserving.
So you're saying it's not even a little correct, it sounds like. Can you give some examples of non-interface-preserving widening conversions?
T * to void *, derived * to base *, int (32 bit) to double (64 bit), T * to const T *, etc. Widening conversions are in the direction of the supertype, hence they often have a subset of the interface of source type. The corresponding narrowing of the interface with the widening of the type is not always the case, eg an int to long is effectively interface preserving -- the same set of operators is supported for both types. Kevlin -- ____________________________________________________________ Kevlin Henney phone: +44 117 942 2990 mailto:kevlin@curbralan.com mobile: +44 7801 073 508 http://www.curbralan.com fax: +44 870 052 2289 Curbralan: Consultancy + Training + Development + Review ____________________________________________________________

Kevlin Henney <kevlin@curbralan.com> writes:
In message <uk727uuvv.fsf@boost-consulting.com>, David Abrahams <dave@boost-consulting.com> writes
The conversion to boost::any isn't a widening conversion in the usual sense, though, is it? Normally, a widening conversion preserves not only the information but the *interface* of the original data -- you wouldn't consider an implicit conversion from int to string a widening conversion, would you?
Your assumption is not quite correct: most widening conversions are not interface preserving.
So you're saying it's not even a little correct, it sounds like. Can you give some examples of non-interface-preserving widening conversions?
T * to void *, derived * to base *, int (32 bit) to double (64 bit), T * to const T *, etc. Widening conversions are in the direction of the supertype, hence they often have a subset of the interface of source type. The corresponding narrowing of the interface with the widening of the type is not always the case, eg an int to long is effectively interface preserving -- the same set of operators is supported for both types.
I guess I don't understand the whole concept now. T* -> void* sounds like "narrowing", since it loses information. int -> long never loses information, so "widening" makes sense to me. What is the criterion? -- Dave Abrahams Boost Consulting www.boost-consulting.com

In message <uptbzrvmf.fsf@boost-consulting.com>, David Abrahams <dave@boost-consulting.com> writes
T * to void *, derived * to base *, int (32 bit) to double (64 bit), T * to const T *, etc. Widening conversions are in the direction of the supertype, hence they often have a subset of the interface of source type. The corresponding narrowing of the interface with the widening of the type is not always the case, eg an int to long is effectively interface preserving -- the same set of operators is supported for both types.
I guess I don't understand the whole concept now. T* -> void* sounds like "narrowing", since it loses information. int -> long never loses information, so "widening" makes sense to me. What is the criterion?
All widening conversions lose type (ie interface and usage) information as a trade for widening the range or number of things that they can refer to. In the case of int to long, you have lost the information that it was originally an int, and could fit into an int. In the case of derived * to base * you have lost the information that you were pointing to something that was at least a derived. In the case of T * to void * you have lost the information that you were dealing with something that was at least a T. In both cases we are dealing with "individuals" and we are widening the range or number of individuals we can deal with or refer to. In the int-to-long case we are dealing with values, so the notion of set containment and its relationship to the widen/narrow metaphor is most obvious here. When dealing with pointers we tend to focus on the entities that we are referring to, rather than on the pointer value itself. A void * can refer to a larger set of objects than a given T *, a base * can refer to a larger set of objects than a derived *. However, the same conclusion can be reached by considering the pointers as values rather than as handles to entities: there are more legal values that a void * can take in a program than a T *. Hope that clarifies the terminology. Kevlin -- ____________________________________________________________ Kevlin Henney phone: +44 117 942 2990 mailto:kevlin@curbralan.com mobile: +44 7801 073 508 http://www.curbralan.com fax: +44 870 052 2289 Curbralan: Consultancy + Training + Development + Review ____________________________________________________________

Kevlin Henney wrote:
I guess I don't understand the whole concept now. T* -> void* sounds like "narrowing", since it loses information. int -> long never loses information, so "widening" makes sense to me. What is the criterion?
All widening conversions lose type (ie interface and usage) information as a trade for widening the range or number of things that they can refer to. In the case of int to long, you have lost the information that it was originally an int, and could fit into an int. In the case of derived * to base * you have lost the information that you were pointing to something that was at least a derived. In the case of T * to void * you have lost the information that you were dealing with something that was at least a T.
In both cases we are dealing with "individuals" and we are widening the range or number of individuals we can deal with or refer to. In the int-to-long case we are dealing with values, so the notion of set containment and its relationship to the widen/narrow metaphor is most obvious here. When dealing with pointers we tend to focus on the entities that we are referring to, rather than on the pointer value itself. A void * can refer to a larger set of objects than a given T *, a base * can refer to a larger set of objects than a derived *. However, the same conclusion can be reached by considering the pointers as values rather than as handles to entities: there are more legal values that a void * can take in a program than a T *.
Hope that clarifies the terminology.
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. 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. - Volodya

In message <c1umak$2ou$1@sea.gmane.org>, Vladimir Prus <ghost@cs.msu.su> writes
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 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 :-) 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. Kevlin -- ____________________________________________________________ Kevlin Henney phone: +44 117 942 2990 mailto:kevlin@curbralan.com mobile: +44 7801 073 508 http://www.curbralan.com fax: +44 870 052 2289 Curbralan: Consultancy + Training + Development + Review ____________________________________________________________

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

Kevlin Henney <kevlin@curbralan.com> writes:
Information loss is not the deciding criterion for whether a conversion is narrowing or widening :-)
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.
I'm sympathetic to the idea that any should use implicit conversions, but, really, are you saying that any has a subtype relationship with everything it can hold? If it did, then we could use any_cast on any argument type, right? -- Dave Abrahams Boost Consulting www.boost-consulting.com

In message <uekscadmf.fsf@boost-consulting.com>, David Abrahams <dave@boost-consulting.com> writes
Kevlin Henney <kevlin@curbralan.com> writes:
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.
I'm sympathetic to the idea that any should use implicit conversions, but, really, are you saying that any has a subtype relationship with everything it can hold? If it did, then we could use any_cast on any argument type, right?
I'm not sure that I understand this wording. Is there another way of phrasing this or perhaps a code fragment that would illustrate the point? Thanks, Kevlin. -- ____________________________________________________________ Kevlin Henney phone: +44 117 942 2990 mailto:kevlin@curbralan.com mobile: +44 7801 073 508 http://www.curbralan.com fax: +44 870 052 2289 Curbralan: Consultancy + Training + Development + Review ____________________________________________________________

Kevlin Henney <kevlin@curbralan.com> writes:
In message <uekscadmf.fsf@boost-consulting.com>, David Abrahams <dave@boost-consulting.com> writes
Kevlin Henney <kevlin@curbralan.com> writes:
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.
I'm sympathetic to the idea that any should use implicit conversions, but, really, are you saying that any has a subtype relationship with everything it can hold? If it did, then we could use any_cast on any argument type, right?
I'm not sure that I understand this wording.
Me neither, at this point.
Is there another way of phrasing this or perhaps a code fragment that would illustrate the point?
Maybe not. Could we forget I ever asked the question? ;-) -- Dave Abrahams Boost Consulting www.boost-consulting.com
participants (3)
-
David Abrahams
-
Kevlin Henney
-
Vladimir Prus