|
Boost : |
From: Fernando Cacciola (fcacciola_at_[hidden])
Date: 2001-09-04 11:01:15
----- Original Message -----
From: Gennadiy E. Rozental <rogeeff_at_[hidden]>
To: <boost_at_[hidden]>
Sent: Monday, September 03, 2001 10:45 PM
Subject: [boost] Re: Proposal: statefull objects
> > > Hi, Fernando
> > >
> > > >
> > > > Hi Gennadity,
> > > >
> > > > > Hi, Fernando
> > > > >
> > > > [SNIP]
> > > Look what I sad in
> http://groups.yahoo.com/group/boost/message/16898
> > > Basic idia is by default it should be value-like implementation.
> If
> > > you can't provide Default constructor - use
> different "instantiation"
> > > policy. One alternative could be based on placement new.
> > >
> > I don't think this is a trivial "instantiation" policy.
> > It is an *implementation choice* policy, and I don't think I like
> to let the
> > user choose between implementations unless there is a *significant*
> > difference between choices; which is not the case.
> >
> > Besides, I don't see why you want to have two implementations.
> > It looks like you assume that accessing a variable which is held
> through a
> > pointer using dereferencig is more expensive than accessing the
> variable
> > directly. Actually, unless your compiler optimizer is smart enough
> to keep
> > variables in registers for subsequent accesses, this is not true.
> That is,
> > there isn't any overhead in dereferencing a pointer. In fact, all
> variables
> > are implicitly pointers which are implicitly dereferenced when they
> are
> > accessed (except, as I said, that the optimizer managed to keep
> them in a
> > register, which is very unusual).
> My understanding is that:
>
> Given definition T* v;
> (v internally is an address of ADDRESS)
> access to the value through *v involve 2 steps:
> 1. Access through address in v to get the value address.
> 2. Access through that address to get the value.
>
> Given definition T v;
> (v internally is an address of VALUE)
> access to the value through *v involve 1 step:
> 1. Access through address in v to get the value.
>
> So difference is significant - 2 times more work.
>
Yes, here you are right, and I was wrong. A pointer implementation implies
on extra step.
> Though I agree - the name is bad.
>
> If optional is implemented using pointers because type T does not
> have default constructor, I do not see a reason to use it at all. I
> would rather use plain pointer, where NULL would meen absent value.
> While for build-in types default constructor exist. If user-defined
> class T has a default constructor it is also worth while using
> optional<T> (value implemented) instead T*. You pay with default
> constructor call. You buy value access efficiency.
>
Actually, the point is not if T provides or not a default constructor. The
point is that you may want to bypass it because it might be too expensive.
Typically, you would use a true pointer in this situation, as you expressed,
but that involes an extra allocation/deallocation which is even worse than
the extra dereferencing:
Consider:
class font : windows::LOGFONT
{
font() ; // initialize with sensible defaults.
} ;
Now, suppose you need an array of font records, but you don't want to
initialze every element since you will assign each element with a sensible
value later.
You have 3 choices:
1) vector<font> v(1000);
This calls 1000 unnecesary default constructors.
2) vector< shared_ptr<font> > v(1000)
You will have to call allocation/deallocation for each elemement later.
3) vector< optional<font> > v(1000)
Here, you skip the 1000 ctor calls and the allocation/deallocation
altogether.
Anyway, I understand your concern that for builtin and trivial PODs the
pointer implementation will add unnecesary overhead, but on the other hand,
with non trivial objects optional<> is better than smart_ptr<> in that you
skip the allocation/deallocation.
Since I don't know how to support both versions without complicating things
(I don't like the policy-based approach, at least not yet), I prefer to
support a single class which avoids a potentially expensive ctor call even
at the cost of more expensive value access.
> >
> > Regarding a policy-based debug/release scheme, I'm still trying to
> analyze
> > its large scale engineering impact.
> > I don't really like a traditional preprocessor-based approach, but
> in the
> > meantime I'll be conservative and stick to it.
>
> I agree with you
>
> > > > > [SNIP]
>
> > > C++ allows you to write int i = 3.14. If there is converstion
> between
> > > U and T ( so you can write T t = u;), we should be able to do
> this:
> > > optional<T> t; *t = u; In other case it will become even more
> strict
> > > limitation then DefaultContructable.
> > >
> > Actually, there is no limitation here. If there is a valid
> conversion
> > between U and T you can
> > always do: optional<T> t ; *t = (T)u ;
>
> First of all in this form it is 2 times more work. You will need to
> call T(U) and than T(T).
> That by itself sometimes could be
> inacceptable.
True. I note this as argument (1).
> Another issue is that the difference is handwritten, th
> at will prevent usage of optional in some cases. Example,
>
> Imagine you have existent template function like this:
> template<typename T, typename U>
> foo( T const& t, U const& u ) {
> ...
> if( t != u )
> t = u;
> ...
> }
>
> optional<T> t; // was T* t; where NULL meant absent value
> U u;
>
> ...
>
> if( !t )
> foo( *t, u );
>
> Last line won't compile.
>
I note this as argument (2).
Now,
argument (2) is a template code that performs implicit conversion. It is
essentially equivalent to a templated assignment or copy constructor.
Though this code has the advantage that skips the extra ctor used in the
explicit cast (argument (1)), it is also a *big* source of potential errors
precisely because of the implicit conversion used.
I recommend *strongly* against the kind of code of argument (2), so I won't
support conversion ctor or assigment, even at the expense of the extra call
that you properly noted in (1).
> > Besides,
> > I REALLY DON'T LIKE *THIS KIND* OF IMPLICIT CONVERSIONS.
> >
> > Just as I would recommend anyone never to write
> > int i = 3.14, but eventually, int i = boost::numeric_cast<int>
> (3.14),
> > I won't support this implicit conversions in optional, not even
> those
> > supported by the language.
>
> Well, I may not like implicit conversions very much either. But given
> the fact that language allows that you can't avoid presence of
> implicit convertion in generic transparent type like optional (at
> least I think it should be transparent - should behave like T*) in my
> opinion.
>
IMHO, the language shouldn't allow this, then I won't.
Fernando Cacciola
Sierra s.r.l.
fcacciola_at_[hidden]
www.gosierra.com
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk