
Hi Jim,
I was a little unclear last time: by deep const I mean something<const T> is the correct indication of const, whereas by shallow const I mean const comething<T> is the correct indication of const.
I see. AFAIK, "deep" X means that when an outer object has property X, its inner objects must have X too; while "shallow" X means that the X property of the outer object is not promoted to the inner objects. For example, deep-const for a wrapper W<T> implies that: char const* test ( T const& ) { return 'const' ; } char const* test ( T& ) { return 'mutable' ; } W<T> w; W<T> const wc; test(w); test(wc); should output: const mutable While "shallow-const" implies that the output is: mutable mutable OTOH, *I* call "preserving" X when the value of X for the inner object is independent of the value of X for the outer object. Most template wrappers are preserving in this sense because cv-qualifiers are preserved. For example, preserving-const implies that: W<T> w; W<T const> cw; test(w); test(cw); outputs: mutable const A deep-const wrapper with preserving-const will be such that: W<T> w; W<T> const wc; W<T const> cw; W<T const> const cwc; test(w); test(wc); test(cw); test(cwc); outputs: mutable const const const value_initialized<> is a shallow-preserving-const wrapper: value_initialized<T> w; value_initialized<T> const wc; value_initialized<T const> cw; value_initialized<T const> const cwc; test(w); test(wc); test(cw); test(cwc); outputs: mutable mutable const const Is this the behaviour you want?
Fernando Cacciola wrote:
It would be very confusing if constantness semantics were to change depending on the compiler.
That's true. Broken const semantics are a problem though, especially the conversion operator. What if the working compilers had the member functions (data and operator T &, perhaps operator->) and non working compilers only had get?
This could be done, yes.
For those compilers, I think there would be value to adding operator-> for the invocation of member functions, like boost::optional.
I'm not sure. operator-> generated a long discussion because of its implied pointer semantics. In the case of boost::optional<>, the possibility of the uninitialized state is my corner argument in favot of it. Yet with value_initialized<> I can't see any favorable argument.
I suppose it depends on your POV. I find get(x).f() and x.data().f() much less clear than x->f(). The reason I find the latter more clear is that it is clear that f is a member function in x's type. get(x) required me to find the get by human-driven ADL, and x.data() looks like I'm acting on a part of x.
It's definitely a POV issue. I personally am not so troubled with operator->, but I know my boosters :-) OTOH, get(w) is a very common idiom used by lots of wrappers and, while "x.data()" looks like acting on a part of x, which is exactly what it's doing, "x.data().foo()" OTOH definitely looks like acting on X's data, not X itself. Anyway, data() should be renamed to get() for uniformity with all the other wrappers.
As an alternative, deep-copy would break const expectations in generic functions that take a const T &, but it would preserve it elsewhere, so it may be worth some more thought.
Yes, though value_initalized is a value wrapper, so it must have deep-copy.
should have been "deep const". To rehash, that means deep const semantics do the wrong thing in
template<typename T> void g(const T & a) {/*whatever*/}
Whether its does the wrong thing or not depends on the nature of the wrapper. In general, value-wrappers should be deep-const, and that will do the right thing because T, being a value-wrapper for U, is intended to behave most like U.
but can have the member functions, including conversion, without any worries about older compilers screaming ambiguity.
If you can guarantee proper initialization for any compiler were it is available, I'd say yes, I'm interested.
I will need someone else to do some testing for me. I'll post a candidate soon.
Great
Should it have shallow or deep const semantics?
Deep const, as it is a value wrapper (and not a pointer wrapper)
Huh?
Most value-wrappers are used to extend/shrink an object's interface, or to better define some part of its behavior. In this case, the rest of the wrapped object should be promoted as is to the wrapper so that usage is transparent. In your case, you are fixing an initialization problem, so the wrapper must look like that wrapped in every other respect. This requires deep semantics (const and copy) Fernando Cacciola SciSoft