Topic: non-type args for template funcs


Author: holen@netcom.com (Victor A. Holen )
Date: Tue, 24 May 1994 19:51:38 GMT
Raw View
In article <> bs@alice.att.com (Bjarne Stroustrup) writes:
>ABack@cen.ex.ac.uk (A.Back @ Computer Unit. - University of Exeter. UK) writes
[...]
> > I would like to propose that the extension of being able to have
> > template functions with non-type template arguments.
>
>This has been approved.

 Could somebody explain what "non-type" template arguments have been
approved?  In particular, will I be able to use template arguments to
specify class members, as in:

template<class Parent, class Child, Child* first, Child* next > class Slist {
 public:
   static inline void add(Parent* par, Child* chi) {chi->next= par->first;
          par->first= chi;  }
};


I've posted this example several weeks ago, but got no response.
Perhaps, more luck this time.  I'm attaching that example again, with
the hope that the thing already is on up coming list of enhancements,
or perhaps I missed alternatives (that don't involve
pre-processor). And if not, are there any languages that can handle
this common need elegantly ?
              =============

 >Subject: templates for containers, enhancement

Motivation:
 Time and again, when a project requires efficient containers, you
must hand code each case or write a code generating pre-processor.
  All C++ books I could find, use only very simplified container models
and/or sacrifice efficiency by generating non-intrusive containers.
  While there are many programs where non-intrusive containers are
fine, or even advantageous, there are many application where one can not
afford the extra space nor time they take.
  I see no _elegant_ way to use current C++ mechanisms to build, for
example, link lists where parent can own more than one list of
children, and a child can be in different parent lists.

Proposal:
  Enhance templates to allow identifiers as parameters. At least some
identifiers as in the example below.

// slist.c   example of use
class Pin; class Comp; class Net;

template<class Parent, class Child, Child* first, Child* next > class Slist {
 public:
   static inline void add(Parent* par, Child* chi) {chi->next= par->first;
          par->first= chi;  }
   //del(), iterate(), whatever()...procedures for parent/child slists
};

class Comp {
   Pin* iFirst;    // for input  pin list
   Pin* oFirst;    // for output pin list
 public:
   friend class Slist<Comp, Pin, iFirst, nextByComp>;
   friend class Slist<Comp, Pin, oFirst, nextByComp>;
};

class Pin {
   Pin* nextByComp;
   Pin* nextByNet;
 public:
   friend class Slist<Comp, Pin, iFirst, nextByComp>;
   friend class Slist<Comp, Pin, oFirst, nextByComp>;
   friend class Slist<Net,  Pin, first,  nextByNet>;
};

class Net {
   Pin* first;  // for list of pins in this net
 public:
   friend class Slist<Net,  Pin, first,  nextByNet>;
};

main() {

 Slist<Comp, Pin, iFirst, nextByComp> compIPin;
 Slist<Comp, Pin, oFirst, nextByComp> compOPin;
 Slist<Net,  Pin, first,  nextByNet > netPin;

 Comp* comp;
 Net*  net;
 Pin*  pin;
 //...
 compIPin.add(comp,pin);
 netPin.add(net,pin);
}

Notes:
  I assume a good compiler can optimize a call to netPin.add(net,pin) as
efficient as pin.addNet(net) or addNetPin(net,pin)
--
 victor      holen@netcom.com




Author: ABack@cen.ex.ac.uk (A.Back)
Date: Fri, 20 May 1994 12:43:46 GMT
Raw View
The ARM specifies that template arguments for a template function must
be type arguements:B (Section 14.4 pg 347):

  "All template-arguments for a function template must be type-arguments"

This means it is not possible to write friend template functions and
ordinary template functions at all for template classes with some
non-type template parameters.

So if we had a Stack:

 template <class T, int SIZE>
 class Stack {
 protected:
   T rep[SIZE];
   int ptr;
 public:
   const T& mpop();
 friend const T& fpop(const Stack<T,SIZE>&);
 };

then this is legal: (member function)

 template<class T, int SIZE>
 const T& Stack<T,SIZE>::mpop() {
   return rep[ptr--];
 }

but this is not according to the ARM: (friend function)

 template<class T, int SIZE>
 const T& fpop(const Stack<T,SIZE> s) {
   return s.rep[s.ptr--];
 }

So you are restricted to not using friend functions, this seems
arbitrary and inconsistent.

I would like to be able to write a class:

 template<class T, int ROW, int COL>
 class Matrix {
 protected:
   Vector<T,COL> rep[ROW];
 template<COL2>
 friend Matrix<T,ROW,COL2> operator*(const Matrix<T,ROW,COL>&,
         const Matrix<T,COL,COL2>&);
 };

and have the compiler automatically check for matrix conformancy on
matrix multiplication.

I can re-write my operator* for matrices to be a member function while
giving an unused extra template parameter COL2 to the class itself:

 template<class T, int ROW, int COL, int COL2>
 class Matrix {
 protected:
   Vector<T,COL> rep[ROW];
 public:
   Matrix<T,ROW,COL2,0> operator*(const Matrix<T,COL,COL2,0>&);
 };

This works, but is ugly as I have to give all Matrix class
instantiations a dummy parameter, and have to give the left hand
Matrix (A below) the correct dummy parameter in order to make it
compatible with operator*.

 {
   Matrix<float,2,3,4> A; // need 4 to satisfy
   Matrix<float,3,4,0> B;
   Matrix<float,2,4,0> C;

   C = A * B;   // this
 }

It also means that I have to do by hand the work of deciding on the
template instantiation for COL2, and that a whole class is
instantiated (Matrix<float,2,3,4>) rather than a single function as
would be possible if we could use a non-type template argument to
operator*.

I would like to propose that the extension of being able to have
template functions with non-type template arguments.

This would then make the Matrix example above legal.

I would also like to see it possible to write member functions taking
template arguments, so that things are symmetric; it should be
possible to write a function as either a member function or a friend
function.

So, using the Matrix example it should be possible to provide both
member and friend versions of the operator* function:

[1] friend function:

 template<class T, int ROW, int COL>
 class Matrix {
 //...
 template<COL2>
 friend Matrix<T,ROW,COL2> operator*(const Matrix<T,ROW,COL>&,
         const Matrix<T,COL,COL2>&);
 };

[2] member function:

 template<class T, int ROW, int COL>
 class Matrix {
 //...
 template<COL2>
 Matrix<T,ROW,COL2> operator*(const Matrix<T,COL,COL2>&);
 };

[3] ordinary function:

 template<class T, int ROW, int COL>
 class Matrix {
 //...
 };

 template<COL2>
 Matrix<T,ROW,COL2> operator*(const Matrix<T,ROW,COL>&,
         const Matrix<T,COL,COL2>&);

Anyone know if the current working paper is re-considering the case
for non-type template arguments to template functions.  Are there any
problems with its implementation, or semantics?

The argument that it could lead to code explosion is not necessarily
valid as it would be possible to have a standard dynamic matrix
implementation DMatrix, and use the template Matrix class to provide
compile time checks on matrix conformancy:

 template<class T, int ROW, int COL>
 class Matrix {
 DMatrix<T> rep;

 public:
   inline Matrix() : DMatrix<T>(ROW,COL) {}

   template<COL2>
   inline friend
   Matrix<T,ROW,COL2> operator*(const Matrix<T,ROW,COL>&
           const Matrix<T,COL,COL2>&);
 };

 template<class T, int ROW, int COL>
 inline
 Matrix<T,ROW,COL2> operator*(const Matrix<T,ROW,COL>& A
         const Matrix<T,COL,COL2>& B) {
   Matrix<T,ROW,COL2> R;
   R.rep = A.rep * B.rep;
   return R;
 }

As all the constructor and all of the member functions of Matrix are
declared inline, a good compiler should be able to completely remove
the existance of Matrix and use DMatrix directly, while providing
compile time checking on DMatrix bounds.

Comments??

Adam
--
______________________________________________________________________________
A.Back@exeter.ac.uk (Internet)      (JANET) A.Back@uk.ac.exeter
--
______________________________________________________________________________
A.Back@exeter.ac.uk (Internet)      (JANET) A.Back@uk.ac.exeter




Author: bs@alice.att.com (Bjarne Stroustrup)
Date: 21 May 94 22:14:54 GMT
Raw View

ABack@cen.ex.ac.uk (A.Back @ Computer Unit. - University of Exeter. UK) writes

 > The ARM specifies that template arguments for a template function must
 > be type arguements:B (Section 14.4 pg 347):

 >   "All template-arguments for a function template must be type-arguments"

 > ...

 > I would like to propose that the extension of being able to have
 > template functions with non-type template arguments.

This has been approved.

 > I would also like to see it possible to write member functions taking
 > template arguments,

That is, a template can be a member.

This too has been approved with the restriction that a member
that is a template may not be virtual.


 - Bjarne