Topic: What was the consensus on this...?


Author: Mat Noguchi <msn@glue.umd.edu>
Date: 1999/04/21
Raw View
Siemel Naran wrote:
> But the question is a profound one.  How can we write class Tvector
> in a way that it is not aware of the type of object it holds?  We
> don't want to hardcode specifics of class MyClass into class Tvector.
> In other words, how do we templatize?

Expand the class to be used in the container to accept an initialization object.
E.g:

template <class T>
class initClass { };

class MyClass
{
public:
 myClass(initClass<MyClass>) { ... }
};

template class<MyClass>
{
... //define relevant initialization stuff here.
};

template <class T, class I = initClass<T> >
class MyVector
{
public:
 void push_back(I) { ... };
};

MSN
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: paulp.removethis@ccnet.com (Paul)
Date: 1999/04/13
Raw View
This still strikes me as a rather messy and presumptuous solution
to what is a simple problem. Your solution is either obfuscated
or makes assumptions about how the contained class is designed.

-------------------------------------------------------------------
>
>...
>
>To solve these low-level problems, make a class Tvector.  Usage:
>   Tvector v;
>   v.push_back(3); // creates a MyClass(3) object in place, sets size, etc
>The destructor Tvector::~Tvector() does some wizadry too to ensure
>proper deletion of all objects.
>
>...
>
>If class MyClass is reference counted, then making copies of
>MyClass objects is cheap.  Just copy a pointer and increment
-------------------------------------------------------------------
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: sbnaran@localhost.localdomain (Siemel Naran)
Date: 1999/04/13
Raw View
On 13 Apr 99 05:39:15 GMT, Paul <paulp.removethis@ccnet.com> wrote:

>>To solve these low-level problems, make a class Tvector.  Usage:
>>   Tvector v;
>>   v.push_back(3); // creates a MyClass(3) object in place, sets size, etc
>>The destructor Tvector::~Tvector() does some wizadry too to ensure
>>proper deletion of all objects.

>This still strikes me as a rather messy and presumptuous solution
>to what is a simple problem. Your solution is either obfuscated
>or makes assumptions about how the contained class is designed.

Exactly.  Class Tvector knows that it is dealing with MyClass objects.
Hence, if there is a constructor MyClass::MyClass(int,double), then
there is a function Tvector::push_back(int,double).  My mistake -- I
should have called the class MyClassvector.  (I realized this after
posting.)

But the question is a profound one.  How can we write class Tvector
in a way that it is not aware of the type of object it holds?  We
don't want to hardcode specifics of class MyClass into class Tvector.
In other words, how do we templatize?
   template <class T> class Tvector { ... };
The problem is that we don't know what the signature of
Tvector<T>::push_back(...) is.  Theoretically, it is
   template <class T> void Tvector<T>::push_back(args of class T);

So one solution is to use lazy evaluation:
   template <class T> class Tvector {
      ...
      template <class Expr> void push_back(const Expr& expr) {
         // increase the reserve
         new (d_data+d_size) T(expr.eval());
   };

Here, function Expr::eval() returns a T.  Eg,
   class MyEval { MyClass eval() const { return MyClass(1,2); } };
The return value optimization allows that the return value of function
eval() be created directly in the return space.  But from the placement
new, the return space is precisely the uninitialized space from
the step "// increase the reserve".

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: paulp@ccnet.com (Paul Pedriana)
Date: 1999/04/11
Raw View
A year or so ago I posted a message complaining
about the fact that the C++ standard does not
define a push_back(void) function for containers
like list and vector.

I was wondering what the consensus on this was.

Just to recap, the problem is that vector can be
quite inefficient because it will not allow you to
add something to it without *copying* from another
instance. As the size of the object being contained
gets larger, this deficiency becomes more obvious.
A solution is to simply provide push_back(void) which
simply puts a default-constructed object at the back
of the list.

A number of responses more or less agreed (obviously
the SGI STL people agreed, because they put it into
v3 of their version). Some said that this was a good
design decision on some kinds of safety grounds (to
which I disagree). Others offered solutions that I
could use pointers or other unusual obfuscating constructs.

Paul
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: sbnaran@bardeen.ceg.uiuc.edu (Siemel Naran)
Date: 1999/04/12
Raw View
On 11 Apr 99 07:19:39 GMT, Paul Pedriana <paulp@ccnet.com> wrote:

>Just to recap, the problem is that vector can be
>quite inefficient because it will not allow you to
>add something to it without *copying* from another
>instance. As the size of the object being contained
>gets larger, this deficiency becomes more obvious.
>A solution is to simply provide push_back(void) which
>simply puts a default-constructed object at the back
>of the list.

To effect your push_back(void), one could just do
   std::vector<MyClass> v;
   v.push_back(MyClass()); // same as your proposed v.push_back()
   v[0]=MyClass(3);
Let's suppose that a MyClass()  is cheap     to construct.
Let's suppose that a MyClass(3) is expensive to copy     .
So the above looks attractive because it seems that the MyClass(3)
object can be created in place in v[0].  However, this is
not so.  It is possible that v[0] does not have sufficient
space to hold a MyClass(3) object -- eg, class MyClass is
std::vector<int>, so MyClass(3) needs space for 3 integers whereas
MyClass() has space for 0 integers.  All in all, this means that
the program must call MyClass::MyClass(int) to consruct a
temporary MyClass object, then copy this MyClass object into v[0]
using MyClass::operator=(const MyClass&), and then destroy the
temporary.  All in all, this is likely to be inefficient.

This way is more efficient:
   std::vector<MyClass> v;
   v.reserve(v.size()+1u);
   new (&v[0]) MyClass(3);
The problem is that it is rather low-level.  The placement new
syntax is complex, and it is easy for clients to accidentally
mess it up.  And consider this
   v.size(); // result is 0, whereas we'd like it to be 1
And what about deletion
   v[0].~MyClass();

To solve these low-level problems, make a class Tvector.  Usage:
   Tvector v;
   v.push_back(3); // creates a MyClass(3) object in place, sets size, etc
The destructor Tvector::~Tvector() does some wizadry too to ensure
proper deletion of all objects.

To templatize, what we need is lazy evaluation.
   Tvector<MyClass> v;
   v.push_back(MyClass(3));
      // in our world, MyClass(3) not evaluated!
      // call is to Tvector<MyClass>::push_back(unevaluated_expression expr)
      // at some point, push_back forces evaluation of 'expr'

The implementation of push_back might look like this:
   template <class T>
   void Tvector<T>::push_back(unevaluated_expression expr) {
      d_data.reserve(d_size+1);
      new (d_data+d_size) T(expr);
      d_size++;
   }

One can get lazy evaluation by using a template class for 'expr'.
A few months ago on comp.lang.c++.moderated, Fergus H. had a
demonstration of this.


>A number of responses more or less agreed (obviously
>the SGI STL people agreed, because they put it into
>v3 of their version). Some said that this was a good
>design decision on some kinds of safety grounds (to
>which I disagree). Others offered solutions that I
>could use pointers or other unusual obfuscating constructs.

If class MyClass is reference counted, then making copies of
MyClass objects is cheap.  Just copy a pointer and increment
an integer counter!
   std::vector<MyClass> v;
   v.push_back(MyClass(3));
      // create a temporary MyClass object using MyClass::MyClass(int)
      // copy this object into v using MyClass::MyClass(const MyClass&)
      // destroy the temporary
If MyClass is reference counted, the call to MyClass's copy constructor
is a joke.  And the destruction of the temporary is basically a no-op
-- ie, also a joke.

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]