 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Vladimir Grigoriev Guest
|
Posted: Tue May 08, 2007 5:08 pm Post subject: A philosophical question about inconsistence between buitin |
|
|
A philosophical question about inconsistence between built-in types and
user-defined types. :)
Assume that I am writing a container as a simple array.
const int DefaultSize = 10;
template < typename T >
class Array
{
public:
Array( int size = DefaultSize );
Array( const Array &rhs );
~Array(){ delete [] pType; }
// Other methods...
private:
T *pType;
int itsSize;
};
And I have a class that mimics integers
class TInt
{
public:
TInt(): intValue( 0 ){}
TInt( int theValue ): intValue( theValue ){}
TInt & operator=( const TInt &rhs )
{
intValue = rhs.intValue;
return *this;
}
friend std::ostream & operator<<( std::ostream & out, const TInt &
rhs )
{
out << rhs.intValue;
return out;
}
// Other methods and operators...
private:
int intValue;
};
The constructor of the Array is
template < typename T >
Array<T>::Array( int size ): itsSize( size )
{
pType = new T[size];
}
Thus if I will declare in main()
Array< TInt > UserIntArray;
I will get an *initialized* array each element of which will have 0 as a
value. For each TInt element of the Array the TInt constructor will be
called only once.
Assume that now I am declaring Array of int;
Array< int > BuiltinIntArray;
I will get *uninitialized* array. Very bad!. So to have an initialized
array of built-in types I should to change the Array constructor.
template < typename T >
Array<T>::Array( int size ): itsSize( size )
{
pType = new T[size];
for ( int i = 0; i < size; i++ ) pType[i] = 0; // additional statement
}
In this case after declaring
Array< int > BuiltinIntArray;
I will get an initialized array at last.
But what about user-defined type?
For user-defined type (TInt) instead of calling its constructor only once
for each element of the array I will get that 1) the default constructor
will be called for each element; 2) the constructor with parameter will be
called to change 0 to class TInt for each element; 3) the assignment
operator will be called to assign a temporar TInt object to each element of
the array. Very bad! :)
Vladimir Grigoriev |
|
| Back to top |
|
 |
Chris Uzdavinis (TeamB) Guest
|
Posted: Tue May 08, 2007 5:47 pm Post subject: Re: A philosophical question about inconsistence between bui |
|
|
"Vladimir Grigoriev" <vlad.moscow (AT) mail (DOT) ru> writes:
| Quote: | template < typename T
Array<T>::Array( int size ): itsSize( size )
{
pType = new T[size];
for ( int i = 0; i < size; i++ ) pType[i] = 0; // additional statement
}
In this case after declaring
Array< int > BuiltinIntArray;
I will get an initialized array at last.
But what about user-defined type?
|
In your code above, the user defined type would be first default
constructed, and then have 0 assigned to it. This may or may not
compile, depending on what operations type T supports.
| Quote: | For user-defined type (TInt) instead of calling its constructor only once
for each element of the array I will get that 1) the default constructor
will be called for each element; 2) the constructor with parameter will be
called to change 0 to class TInt for each element; 3) the assignment
operator will be called to assign a temporar TInt object to each element of
the array. Very bad!
|
The way this is can be handled more uniformly is to allocate an array
of properly aligned bytes, and then iteratively use placement new to
instantiate each element in that array. If any object constructor
throws, you must destroy all the previously constructed elements
before exiting your Array constructor.
In your Array destructor you'd have to explicitly invoke the
destructor on each element, and then delete[] the array of bytes.
--
Chris (TeamB); |
|
| Back to top |
|
 |
Vladimir Grigoriev Guest
|
Posted: Tue May 08, 2007 6:26 pm Post subject: Re: A philosophical question about inconsistence between bui |
|
|
"Chris Uzdavinis (TeamB)" <chris (AT) uzdavinis (DOT) com> wrote in message
news:864pmnv5x0.fsf (AT) explicit (DOT) atdesk.com...
| Quote: |
In your code above, the user defined type would be first default
constructed, and then have 0 assigned to it. This may or may not
compile, depending on what operations type T supports.
|
Well, and what to do if T does not support assignment operator? How to join
initialization of built-in types and user-defined types in one container?
| Quote: | The way this is can be handled more uniformly is to allocate an array
of properly aligned bytes, and then iteratively use placement new to
instantiate each element in that array. If any object constructor
throws, you must destroy all the previously constructed elements
before exiting your Array constructor.
|
It makes things more complicated!
Vladimir Grigoriev. |
|
| Back to top |
|
 |
Chris Uzdavinis (TeamB) Guest
|
Posted: Tue May 08, 2007 6:41 pm Post subject: Re: A philosophical question about inconsistence between bui |
|
|
"Vladimir Grigoriev" <vlad.moscow (AT) mail (DOT) ru> writes:
| Quote: | "Chris Uzdavinis (TeamB)" <chris (AT) uzdavinis (DOT) com> wrote in message
news:864pmnv5x0.fsf (AT) explicit (DOT) atdesk.com...
In your code above, the user defined type would be first default
constructed, and then have 0 assigned to it. This may or may not
compile, depending on what operations type T supports.
Well, and what to do if T does not support assignment operator? How
to join initialization of built-in types and user-defined types in
one container?
|
You either must document your requirements (requiring users to adhere
to them), or you don't do the assignment and drop that requirement entirely.
NOTE: default 'construction' of an integer will initialize it to zero.
int x1 = int(); // x1 is guaranteed to be 0
int * x2 = new int(); // x2 is guaranteed to point to an (int) 0.
However:
int x1; // uninitialized
int * x2 = new int; // points to uninitialized data
| Quote: | It makes things more complicated!
|
That depends. You are simply being explicit in the construction and
destructon of the elements you're managing, but you handle ALL of them
in a consisten manner, and do NOT need to do any post-construction
assignment.
The only alternate I can think of (maintaining your original approach)
is to specialize your Array based on some type-traits of T. If T is a
built-in type, then you allocate the array and then memset the data to
0. If it's not a built-in type, you allocate the array and rely on
the default constructors to be run for you.
This approach seems complicated in its own regard, for different
reasons: you have to determine if T is built-in (using boost or tr1
type_traits library can help), and then you need to specialize
templates for different cases. The cases are individually easier, but
it's more code and they're handled differently.
With my suggestion, you have one implementation that works for
everything. Like always, there is always a trade-off.
--
Chris (TeamB); |
|
| Back to top |
|
 |
Sergiy Kanilo Guest
|
Posted: Wed May 09, 2007 2:52 am Post subject: Re: A philosophical question about inconsistence between bui |
|
|
"Vladimir Grigoriev" <vlad.moscow (AT) mail (DOT) ru> wrote in message
news:46406795 (AT) newsgroups (DOT) borland.com...
| Quote: | A philosophical question about inconsistence between built-in types and
user-defined types.
|
[...]
| Quote: | class TInt
{
public:
TInt(): intValue( 0 ){}
TInt( int theValue ): intValue( theValue ){}
TInt & operator=( const TInt &rhs )
{
intValue = rhs.intValue;
return *this;
}
[...]
private:
int intValue;
};
[...]
array of built-in types I should to change the Array constructor.
template < typename T
Array<T>::Array( int size ): itsSize( size )
{
pType = new T[size];
for ( int i = 0; i < size; i++ ) pType[i] = 0; // additional statement
}
For user-defined type (TInt) instead of calling its constructor only once
for each element of the array I will get that 1) the default constructor
will be called for each element; 2) the constructor with parameter will be
called to change 0 to class TInt for each element; 3) the assignment
operator will be called to assign a temporar TInt object to each element
of the array. Very bad!
|
Which is exactly how you ask TInt to behave.
If you don't want initialization by TInt constructor (so it more resemple
int),
it should be
TInt::TInt(){}
If you do not want creating temporary when assigning to an object
(even if good compiler will optimize extra copying) then provide
proper assignment operator
TInt& TInt::operator=( int rhs )
{
intValue = rhs;
return *this;
}
Cheers,
Serge
PS: copuple workarounds
1) new int[10]() should initialize array with zeros
(unfortunately it is in theory only; AFAIK BCB or BDS do not
support this feature)
2) std::vector has exactly the functionality you want for
your Array constructor (for TInt as is originally written),
without extra assignments and temporary objects |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|