Link to home
Start Free TrialLog in
Avatar of barryc
barryc

asked on

Can class Foo derive from a template on Foo?

The following code has a class which derives from a template on itself:

================================================================================
template <class T>
class Foo
{
};

class Bar : public Foo<Bar>
{
};

int main()
{
  Bar b;  
}
================================================================================

This code compiles under SunPro CC 4.2 and 5.0 and g++ 2.8.1, but is it ANSI
compatible?  Basically, I'm afraid that deriving from a template on yourself
may not be portable and only works because of the way the compilers are
performing template generation.  In the first case the template class Foo makes
no reference to members of the templated class "T".  The following example
changes that:

================================================================================
template <class T>
class Foo
{
public:
  static int foo(void) {
    return T::bar();
  }
};

class Bar : public Foo<Bar> {
public:
  static int bar(void) {
    return 0;
  }
};

int main(void) {
  Bar b;
   cout << Bar::foo() << endl;
}
================================================================================

This second example compiles under SunPro CC 4.2 and 5.0 and g++ 2.8.1.  The
template references a member of the templated class "T".  As you can see,
Bar is deriving from Foo<Bar> where Foo<Bar> is then referencing a member of
a class which cannot exist until Foo<Bar> exists.  The problem is conceptually
cyclic and yet it compiles just fine.

What I want to know is:
      1) Is the first example legal by the ANSI specification or are the
         compilers allowing it because of a flaw in their implementation.
      2) Is the second example legal by the ANSI specification or are the
         compilers allowing it because of a flaw in their implementation.

If the above constructs are legal then they can be very powerful, if not then
I don't want to rely on them since they may not be supported by the compiler
at some point in the future.
ASKER CERTIFIED SOLUTION
Avatar of nietod
nietod

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of nietod
nietod

Thus neither example is legal.  I am surprised that any compiler can handle it.  I suspect that if you used a non-static data member, like

template <class T>
class Foo
{
   T t;
};

class Bar : public Foo<Bar>
{
};

The compiler will choke on it.  If it doesn't fail in compilation, it certainly would fail numerious run-time tests.

>> I don't want to rely on them since they may
>> not be supported by the compiler
>> at some point in the future.
I wouldn't rely on them on a compiler that supports them!  It may be "fixed" someday, and even if not, flaws in its implementation of this "feature" may be revealed later.

Let me know if you have any questions.
Avatar of barryc

ASKER

The standard states:
The  class-name  in  a  base-specifier  shall  not  be an incompletely defined
class (_class_)

So... if I did something like this:

================================================================================
class Foo; // foward declare

class Bar {
public:
  friend Foo;

  Bar(void) {}

  Bar(const Bar& b) {}

  ~Bar(void);

  Bar& operator=(const Bar& b) { return (*this); }

  Foo* getFoo(void) const;
};

class Bar2 : public Bar {
  ...
};
================================================================================
Is Bar an "incompletely" defined class in this example?  The reason I ask is
because one of my potential uses for the class deriving from a template on
itself requires no actual knowledge of what the templated class provides,
but only requires the existence of the type "T" and the ability to make "T"
into a friend class.  The above example uses a forward declaration which is
somewhat similar in concept to the first template example.

One potential use is the ability to make a class that has public constructors,
but can NOT be derived from.  This is similar to a class which is tagged as
"final" in Java.  Here is how it works....

================================================================================
template <class T>
class Final {
public:
  friend T;

private:
  inline Final(void) { }
  Final(const Final<T>&) { }
  virtual ~Final(void) { }  
  Final<T>& operator=(const Final<T>&) { return *this; }
};

class A : public virtual Final<A> // virtually derive so any sub-class must
{                          // also construct "Final<A>" base class
public:
  A(void) { }  
  A(const A&) {}
  ~A(void) {}
  A& operator=(const A&) { return *this; }
};

// attempt to construct another class deriving from A
class B : public A {
public:
  B(void) { }        // this will fail since B cannot access Final<A>::Final()
  B(const A&) {}  // this will fail since B cannot access Final<A>::Final()
  ~B(void) {}
  B& operator=(const B&) { return *this; }
};

================================================================================


Avatar of barryc

ASKER

The standard states:
The  class-name  in  a  base-specifier  shall  not  be an incompletely defined
class (_class_)

So... if I did something like this:

================================================================================
class Foo; // foward declare

class Bar {
public:
  friend Foo;

  Bar(void) {}

  Bar(const Bar& b) {}

  ~Bar(void);

  Bar& operator=(const Bar& b) { return (*this); }

  Foo* getFoo(void) const;
};

class Bar2 : public Bar {
  ...
};
================================================================================
Is Bar an "incompletely" defined class in this example?  The reason I ask is
because one of my potential uses for the class deriving from a template on
itself requires no actual knowledge of what the templated class provides,
but only requires the existence of the type "T" and the ability to make "T"
into a friend class.  The above example uses a forward declaration which is
somewhat similar in concept to the first template example.

One potential use is the ability to make a class that has public constructors,
but can NOT be derived from.  This is similar to a class which is tagged as
"final" in Java.  Here is how it works....

================================================================================
template <class T>
class Final {
public:
  friend T;

private:
  inline Final(void) { }
  Final(const Final<T>&) { }
  virtual ~Final(void) { }  
  Final<T>& operator=(const Final<T>&) { return *this; }
};

class A : public virtual Final<A> // virtually derive so any sub-class must
{                          // also construct "Final<A>" base class
public:
  A(void) { }  
  A(const A&) {}
  ~A(void) {}
  A& operator=(const A&) { return *this; }
};

// attempt to construct another class deriving from A
class B : public A {
public:
  B(void) { }        // this will fail since B cannot access Final<A>::Final()
  B(const A&) {}  // this will fail since B cannot access Final<A>::Final()
  ~B(void) {}
  B& operator=(const B&) { return *this; }
};

================================================================================


>> Is Bar an "incompletely" defined class in this example?
No.  That is fine.  I'll t to find the definition for "incompletely defined"  But basically it means that the non-static data members must be defined and that the base classes must be defined.  Member procedures and statid data members need only be declared, not defined.
The actuall definition is quit a bit different than what I said, but it amounts to the same thing.  (Actually the official definition handles use of the class within the body of the class definition, which mine did not.)

****************************

A class is considered a completely-defined object type (_basic.types_) (or  complete  type)  at the closing } of the class-specifier.  Within the class member-specification, the  class  is  regarded  as  complete within function  bodies,  default arguments and constructor ctor-initializers (including such things in nested classes).  Otherwise it  is regarded as incomplete within its own class member-specification.
This is a little closer to what I was saying
******************
Non-static  (_class.static_)  members  that are class objects shall be objects of previously defined classes.   In  particular,  a  class  cl shall  not contain an object of class cl, but it can contain a pointer or reference to an object of class cl.

>> The above example uses a forward declaration
>> which is somewhat similar in concept to the first
>> template example
But it is different in an important way.  Foo is incompletely defined, but Foo is not a non-static data member and Foo is not a base class.  Foo is only a friend and also a return value from a function, both of which are okay  (it would also be okay to have a Foo parameter to a member function, to have a pointer to a Foo or to have a reference to a Foo.)

Think of it this way.  At the close of the class definition ("}"), the compiler must be able to "lay out" an object of the class in memory.  That is, it must know the size and nature of each "piece" of data that is stored in an object.  The pieces of data that are stored in an object are its non-staitc data members  (static data members aren't stored in the object) and its base classes.  So these things must be completely defined.  Note that if can have pointers and references to incompletely defined classes, because it knows the size of a pointer or reference (the size doesn't depend on the actuall definition of the thing refered to).  It can also have meber functions that take an incompletely defined class as a parameter or return value, because these do not actually get stored inside an object of the class.

>> One potential use is the ability to make
>> a class that has public constructors,
>> but can NOT be derived from
In C++ you can do this by making the destructor private.  However this also makes it impossible to directly create objects.  The only way to create such an object is they through a factory function or a static member function.
Avatar of barryc

ASKER

So does that mean Final<A> is incomplete in the example above when A attempts
to derive from it?  The closing brace of Final<A> is generated template code.
Final<A> does not refer to any members of A, but simply uses it in a way
analagous to a forward declaration.  For example:

================================================================================
#include <iostream.h>

class Foo;
class Bar;

class Foo {
public:
  friend Bar;
  Foo(void) { }
  Foo(const Foo&) { }
  ~Foo(void) { }
  Foo& operator=(const Foo&) { return *this; }
  virtual Bar* getBar(void) const { return (Bar*) 0; }
};

class Bar : public Foo {
public:
  Bar(void) { }
  Bar(const Bar&) { }
  ~Bar(void) { }
  Bar& operator=(const Bar&) { return *this; }
  virtual Bar* getBar(void) const { return new Bar(); }
  const char* getMessage(void) const { return "Hello"; }
};

int main(int argc, const char* argv[]) {
  Bar b;

  Bar* bPtr = b.getBar();
  cout << bPtr->getMessage() << endl;
  delete bPtr;
 
  return 0;
}
================================================================================

This code does a very similar thing without any templates whatsoever.  
This code also works under SunPro 4.2 and 5.0 as well as g++ 2.8.1.  At the
point that Bar is declared Foo is complete since we have reached the closing
"}".

If Foo is complete then by the same reasoning shouldn't Final<A> be complete
when A is declared?  Final<A> only uses the type "A", but no members of class A.
Template generation must work so that the template is considered complete when
first used or else any use of a template would probably fail.  My particular
use of the template is as a base class in a rather unorthodox way.

It seems that whatever Sun and g++ did to guarantee the template is complete
just prior to its first use, must have led to a bug where the template can
reference members of a class which is not yet complete.

If I change the above code so that "Foo::getBar(void) const" returns
"new Bar();" instead of a null pointer, then I get a compile error.  
For example, the following code:

================================================================================

#include <iostream.h>

class Foo;
class Bar;

class Foo {
public:
  friend Bar;
  Foo(void) { }
  Foo(const Foo&) { }
  ~Foo(void) { }
  Foo& operator=(const Foo&) { return *this; }
  virtual Bar* getBar(void) const { return new Bar(); }
};

class Bar : public Foo {
public:
  Bar(void) { }
  Bar(const Bar&) { }
  ~Bar(void) { }
  Bar& operator=(const Bar&) { return *this; }
  virtual Bar* getBar(void) const { return new Bar(); }
  const char* getMessage(void) const { return "Hello"; }
};

int main(int argc, const char* argv[]) {
  Bar b;

  Bar* bPtr = b.getBar();
  cout << bPtr->getMessage() << endl;
  delete bPtr;
 
  return 0;
}
================================================================================

Does not work:

SunPro CC 4.2 yields: Error: The type "Bar" is incomplete.
SunPro CC 5.0 yields: Error: The type "Bar" is incomplete.
GNU g++ 2.8.1 yields: invalid use of undefined type `class Bar'

What do you think nietod?  Do either of the above examples compile under
Visual C++?  Does one fail or both?  Is my reasoning sound that the class
Final<A> is complete when used as a base class for A?

Thanks in advance for the help!
It seems from the standard that your basic example is illegal. Bar is not completely defined, thus Foo<Bar> is not completely defined.

>> So does that mean Final<A> is incomplete in the
>> example above when A attempts
>> to derive from it?
Yes.  Final<A> is incomplete because A is incomplete.

From the standard
*********************
A template-id that names a class template explicit specialization that has been declared but not defined can be used exactly like  the names of other incompletely-defined classes (_basic.types_).  [Example:
          template<class T> class X; // X is a class template
          template<> class X<int>;

          X<int>* p; // ok: pointer to declared class X<int>
          X<int> x; // error: object of incomplete class X<int>
   --end example]
***********************

Thus Final<A> is an incomplete type if A is an incomplete type and as an incomplete type, Final<A> may not be a base class.

>> This code does a very similar thing without
>> any templates whatsoever

your Foo and Bar example is fine.  But it is not analogous to the problem.  (i.e it is not similar.)  You are not using an incomplete type as a base class.  Tht is a crucial difference.  You could replace Bar with a template instanciated for Foo and it would still work.  But you cannot replace Foo with a template instanciated for Bar.  You can't do that because it can't be a base class for Bar.


>> If Foo is complete then by the same reasoning
>> shouldn't Final<A> be complete
>> when A is declared?
Foo is complete after its closing brace ("}").  It may then be used as a base class (and other ways too).   Thus Foo can be a base class for Bar.  But Final<A> is incomplete at its closing "}" it is complete only after "A" is also complete.


>> If I change the above code so that
>> "Foo::getBar(void) const" returns
?? "new Bar();" instead of a null pointer, then I get a
>> compile error.  

And you should.  At the point at which you use "new Bar()" the compile knows only that Bar is a class.  It has not seen the class declaration.  So it does not know if the class can be instanciated.  it may be an abstract class, it may have private constructors/destructor/  You are also trying to use the class's default constructor, but it may not even have a default constructor.

The only way to make this work is to declare the getBar() function inside the Foo class, but not define it there.  The function must be defined after Bar is fully declared.  like

class Foo;
class Bar;

class Foo {
public:
  friend Bar;
  Foo(void) { }
  Foo(const Foo&) { }
  ~Foo(void) { }
  Foo& operator=(const Foo&) { return *this; }
  virtual Bar* getBar(void) const; // Declare, but delay define.
};

class Bar : public Foo {
public:
  Bar(void) { }
  Bar(const Bar&) { }
  ~Bar(void) { }
  Bar& operator=(const Bar&) { return *this; }
  virtual Bar* getBar(void) const { return new Bar(); }
  const char* getMessage(void) const { return "Hello"; }
};

virtual Bar* Foo::getBar(void) const { return new Bar(); }  // define
#include <iostream.h>

             class Foo;
             class Bar;

             class Foo {
             public:
               friend Bar;
               Foo(void) { }
               Foo(const Foo&) { }
               ~Foo(void) { }
               Foo& operator=(const Foo&) { return *this; }
               virtual Bar* getBar(void) const { return (Bar*) 0; }
             };

             class Bar : public Foo {
             public:
               Bar(void) { }
               Bar(const Bar&) { }
               ~Bar(void) { }
               Bar& operator=(const Bar&) { return *this; }
               virtual Bar* getBar(void) const { return new Bar(); }
               const char* getMessage(void) const { return "Hello"; }
             };

             int main(int argc, const char* argv[]) {
               Bar b;

               Bar* bPtr = b.getBar();
               cout << bPtr->getMessage() << endl;
               delete bPtr;
               
               return 0;
             }

compiles--as it should.

#include <iostream.h>

             class Foo;
             class Bar;

             class Foo {
             public:
               friend Bar;
               Foo(void) { }
               Foo(const Foo&) { }
               ~Foo(void) { }
               Foo& operator=(const Foo&) { return *this; }
               virtual Bar* getBar(void) const { return new Bar(); }
             };

             class Bar : public Foo {
             public:
               Bar(void) { }
               Bar(const Bar&) { }
               ~Bar(void) { }
               Bar& operator=(const Bar&) { return *this; }
               virtual Bar* getBar(void) const { return new Bar(); }
               const char* getMessage(void) const { return "Hello"; }
             };

             int main(int argc, const char* argv[]) {
               Bar b;

               Bar* bPtr = b.getBar();
               cout << bPtr->getMessage() << endl;
               delete bPtr;
               
               return 0;
             }

Does not compile--as it should not  The error is

D:\tstcon\tstcon.cpp(55) : error C2512: 'Bar' : no appropriate default constructor available

which is 1 of several possible errors it could report.  


OUCH

             template <class T>
             class Final {
             public:
               friend T;

             private:
               inline Final(void) { }
               Final(const Final<T>&) { }
               virtual ~Final(void) { }  
               Final<T>& operator=(const Final<T>&) { return *this; }
             };

             class A : public virtual Final<A> // virtually derive so any sub-class must
             {   // also construct "Final<A>" base class
             public:
               A(void) { }  
               A(const A&) {}
               ~A(void) {}
               A& operator=(const A&) { return *this; }
             };


Does compile.  This is distressing.  However if the Final<> class stores a T in it (stores an A) then this does not compile--which is good!  I'm not sure why it is allowed--if it is allowed--in the example.


The same is true of the original example

template <class T>
             class Foo
             {
             public:
               static int foo(void) {
                 return T::bar();
               }
             };

             class Bar : public Foo<Bar> {
             public:
               static int bar(void) {
                 return 0;
               }
             };

             int main(void) {
               Bar b;
                cout << Bar::foo() << endl;
             }

It does compile.  If I add a T into Foo<> then it fails, as it must.  I will need to do some more research, but cannot at this time.  
Similar results on gcc 2.8.1 Apparently the construct us allowed if no Bar objects are used as member, or, Bar's layout does not affect its layout ;)
That's what I'm finding, but I'd like some "proof"--as does barryc I'm sure.  
Avatar of barryc

ASKER

I appreciate all of the input and help I am getting on this question (especially from nietod who has devoted alot of time).  Since the question is requiring more research and is not all that "open-and-shut" I am going to increase the number of points that it is worth to you for researching the answer further.  I guess the basic question is:

Is a template class defined as:

template <class T> Foo { . . . };

complete if the templated type "T" is not complete in the case where the definition of Foo<T> does not require "T" to be a complete type since it only uses "T" as reference types, pointer types, or as a friend.

I appreciate all of the input and help I am getting on this question
(especially from nietod who has devoted alot of time).  Since the question is
requiring more research and is not all that "open-and-shut" I am going to
increase the number of points that it is worth to you for researching the
answer further.  I guess the basic question is:

********************************************************************************
Is a template class defined as:

template <class T> Foo { . . . };

complete if the templated type "T" is NOT complete in the case where the
definition of Foo<T> does NOT require "T" to be a complete type (i.e.: Foo<>
only uses the templated type "T" as a reference, pointer, or friend)?

********************************************************************************

BTW: Your example of declaring "Bar* getBar(void) const" and then defining it
later is something I hadn't thought of.  Conceptually, it seems like it should
fail since class Foo is using the constructor from a derived class that cannot
be defined without it, but it works.
Avatar of barryc

ASKER

Hmm.... why are all my comments being posted twice or with repeated text?  Maybe I shouldn't press that reload button on Netscape??
A practical BTW, will your stuff work with the following construct:

template<class T>
class Foo {};

class Bar;

class Bar : public Foo<Bar*> {};

This would take some of the heat away.
>> Maybe I shouldn't press that reload
>> button on Netscape??
corrrect (don't reload after submitting.)

>> it seems like it should fail since class
>> Foo is using the constructor from a derived
>> class that cannot be defined without it, but
>> it works.
It works because at that point in the program, the constructor for Bar is _declared_  whether or not it is defined at that point does not matter.  In your example--the one that didn't work--the constructor for the Bar was not even declared when you tried to use it.

haven't found anything yet (found 1 or 2 things that wer close, but not quit 100% conclusive).  Have to quit for a while.
Does the template instantiation require a complete type?

Consider this:

/**********************************************************************/
template< class T > class Gnat {};

class Gnu;

void f()
{
  Gnat<Gnu> g;
}
/**********************************************************************/

Legal? If so (gcc is happy) then
  class Bar : public Gnat<Bar> {};
is legal and the problem remaining is how Gnat uses its T. If a concrete T is not a completely defined class, then Gnat may not use it as such, ie
  template<class T> class Gnat
  {
     T t; // illegal
     T* p; // legal
  }
Avatar of barryc

ASKER

Adjusted points to 600
As far as I can tell--if you can trust me at this point--it is legal  But i won't swear to it.   As best as I can understand, a class is complete at its closing "}" (as posted before).  The is true of a regualr class or a template class.  Thus in

template <class T>
class Foo
{
};

class Bar : public Foo<Bar>
{
};

the class Foo<> is complete at the end of its "}" and the completness of its template arguments is not necessary to consider the class complete (the completenss of the template paremetes are important in many other cases, but not in considering the template class itelf complete)

So for that reason, it is legal to use Foo<> as a base class for any class.

Next can Foo<Bar> be used when Bar is not yet defined.  That is what kangaroo was asking.  I beleive the answer is yes, however I cannot find it spelled out in the standard.  But I can find examples of it--more or less.  More research is needed, but it seems that a template argument can be used that is not completely defined as long as the template does not need to use the argument in a "cocnrete" way at the point of instanciation.  This has to do with the rules that control error checking in templates.  In particular the fact that some errors may depend on an argument's type and the error can therefore be reported only once the template is instanciated for a particular type, for example

Knowing which names are type names allows the syntax of every template
  definition  to  be  checked.  No diagnostic shall be issued for a tem-
  plate definition for which a valid specialization  can  be  generated.
  If no valid specialization can be generated for a template definition,
  and that template is not instantiated, it is  unspecified  whether  or
  not  an implementation is required to issue a diagnostic.  [Note: if a
  template is instantiated, errors will be diagnosed  according  to  the
  other rules in this Standard.  Exactly when these errors are diagnosed
  is a quality of implementation issue.  ] [Example:
          int j;
          template<class T> class X {
                  // ...
                  void f(T t, int i, char* p)
                  {
                          t = i;  // diagnosed if X::f is instantiated
                                  // and the assignment to t is an error
                          p = i;  // may be diagnosed even if X::f is
                                  // not instantiated
                          p = j;  // may be diagnosed even if X::f is
                                  // not instantiated
                  }
                  void g(T t) {
                          +;      // may be diagnosed even if X::g is
                                  // not instantiated
                  }
          };
   --end example]
             }
Avatar of barryc

ASKER

nietod: Did you want to do more research and post more to this thread before I
accept your answer?

From what you have posted from the standard it seems that it using Foo<Bar> as
a base class for Bar is most likely legal so long as Foo<Bar> does not use Bar
in a concrete way.

Now, what if Foo<Bar> does use Bar in a concrete way in its implementation,
but NOT in its declaration.  Consider the case were NO templates are involved:
================================================================================
#include <iostream.h>

class Bar;

class Foo {
public:
  Foo(void);
  Foo(const Foo& f);
  virtual ~Foo(void);
  Foo& operator=(const Foo& f);
 
  virtual Bar* getBar(void) const;
  virtual int getValue(void) const;
};

class Bar : public Foo {
public:
  Bar(int x = 0);
  Bar(const Bar& b);
  virtual ~Bar(void);
  Bar& operator=(const Bar& b);

  virtual Bar* getBar(void) const;
  virtual int getValue(void) const;

private:
  int x_;
};

Foo::Foo(void) {}
Foo::Foo(const Foo& f) {}
Foo::~Foo(void) {}
Foo& Foo::operator=(const Foo& f) { return *this; }
Bar* Foo::getBar(void) const { return new Bar(); }
int Foo::getValue(void) const { return 0; }

Bar::Bar(int x) : Foo(), x_(x) { }
Bar::Bar(const Bar& b) : Foo(b), x_(b.x_) { }
Bar::~Bar(void) { }
Bar* Bar::getBar(void) const { return new Bar(x_); }
int Bar::getValue(void) const { return x_; }

int main(int argc, const char* argv[])
{
  Foo f;
  Bar b(4);
  Foo* fptr = &b;
  Bar* bptr = fptr->getBar();

  cout << bptr->getValue() << endl;
  return 0;
}

================================================================================

The compile recognizes Bar as a type, but not a complete type, and thus it is
used in the definition of Foo.  Once Foo's closing "}" has been reached then
it _is_ a complete type.  Bar then uses Foo as a base class.  After Bar is
complete we define the methods for Foo and Bar and since both are complete
classes then both can be used in a concrete way.  NOTE: The output of the
compiled program is "4".

Now, let's change things up....

================================================================================
#include <iostream.h>

template <class T>
class Foo {
public:
  Foo(void);
  Foo(const Foo<T>& f);
  virtual ~Foo(void);
  Foo& operator=(const Foo<T>& f);

  virtual T* getObject(void) const;
  virtual int getValue(void) const;
};

class Bar : public Foo<Bar> {
public:
  Bar(int x = 0);
  Bar(const Bar& b);
  virtual ~Bar(void);
  Bar& operator=(const Bar& b);

  virtual Bar* getObject(void) const;
  virtual int getValue(void) const;

private:
  int x_;
};

template <class T> Foo<T>::Foo(void) {}
template <class T> Foo<T>::Foo(const Foo& f) {}
template <class T> Foo<T>::~Foo(void) {}
template <class T> Foo<T>& Foo<T>::operator=(const Foo<T>& f) { return *this; }
template <class T> T* Foo<T>::getObject(void) const { return new T(); }
template <class T> int Foo<T>::getValue(void) const { return 0; }

Bar::Bar(int x) : Foo<Bar>(), x_(x) { }
Bar::Bar(const Bar& b) : Foo<Bar>(b), x_(b.x_) { }
Bar::~Bar(void) { }
Bar* Bar::getObject(void) const { return new Bar(x_); }
int Bar::getValue(void) const { return x_; }

int main(int argc, const char* argv[])
{
  Foo<Bar> f;
  Bar b(4);
  Foo<Bar>* fptr = &b;
  Bar* bptr = fptr->getObject();

  cout << bptr->getValue() << endl;
  return 0;
}

================================================================================

In the template example I have strategically placed the definition of the
template functions _AFTER_ the declaration of class Bar.  BUT, where does the
compiler actually see the template methods defined?  Is that part of the
standard?  If the standard dictates that template code prototypes are
instantiated at first use and the generation of their implementations is
delayed until later then a template may use its templated type in a concrete
way outside of its template class declaration "{ ... }" and not ever worry about
the template type being complete.    NOTE: once again, the output of the
compiled program was "4".

The final example actually uses the template type in a concrete way PRIOR to
the closing "}" of the template class definition....

================================================================================
class Foo {
public:
  Foo(void);
  Foo(const Foo<T>& f);
  virtual ~Foo(void);
  Foo& operator=(const Foo<T>& f);

  virtual T getObject(void) const;      // returns an object NOT a pointer
  virtual int getValue(void) const;
};

class Bar : public Foo<Bar> {
public:
  Bar(int x = 0);
  Bar(const Bar& b);
  virtual ~Bar(void);
  Bar& operator=(const Bar& b);

  virtual Bar getObject(void) const;
  virtual int getValue(void) const;

private:
  int x_;
};

template <class T> Foo<T>::Foo(void) {}
template <class T> Foo<T>::Foo(const Foo& f) {}
template <class T> Foo<T>::~Foo(void) {}
template <class T> Foo<T>& Foo<T>::operator=(const Foo<T>& f) { return *this; }
template <class T> T Foo<T>::getObject(void) const { return T(); }
template <class T> int Foo<T>::getValue(void) const { return 0; }

Bar::Bar(int x) : Foo<Bar>(), x_(x) { }
Bar::Bar(const Bar& b) : Foo<Bar>(b), x_(b.x_) { }
Bar::~Bar(void) { }
Bar Bar::getObject(void) const { return Bar(x_); }
int Bar::getValue(void) const { return x_; }

int main(int argc, const char* argv[])
{
  Foo<Bar> f;
  Bar b(4);
  Foo<Bar>* fptr = &b;
  Bar b2 = fptr->getObject();

  cout << b2.getValue() << endl;
  return 0;
}
================================================================================

This also compiled (whether or not it is legal) and had the same output ("4").

What I'm trying to get at is that without templates it _IS_ legal to have one
class use another in a concrete way so long as we order the class declarations
and the definitions of the methods in just the right way.  The placement of
the source code is vital to such code working.

With template code, the actual placement of the code will be decided by the
compiler.  A template definition is just a way to tell the compiler "how" to
generate the class, BUT the compiler actually does the work of creating a name
for each instance of a template class and generating that code.  Knowing where
that code is placed in relation to the non-template class code is essential to
understanding what can and cannot be done with templates.

My third example should almost definitely fail.  I can't imagine a way in
which the compiler could "place" the generated code so that an incomplete type
could be used as a return value.

I hope this gives some food for thought.  Thanks.
>> Did you want to do more research and post
>> more to this thread before I accept your
>> answer?
FYI, accepting an answer does no really close a question, the client and experts can continue to post to the question after it has been graded, but I don't remommend you accept an answer until you are satisfied with it as one the question is graded, the experts don't have much "motivation" to provide additional help.  (Although honestly that doesn't seem to be a big problem.)

However, I think I am done.  I can't find anything that spells it out.  But there are examples where template parameters are incompletly defined.  Whether or not they need to be completely defined is determined--as far as I can tell from the examples--in how they are then used by the template at the point of instanciation, following the same rules as if they were not part of a template.   So Foo<> can be a base class of Bar because it is complete and Foo can be instanciated on an incompletely defined Bar.

>> it _IS_ legal to have one class use another
>> in a concrete way so long as we order the class
>> declarations and the definitions of the methods
>> in just the right way.
"concrete" was a term I used its not...official.  What I meant by it was "used in a way that requires a completely defined class".

So, yes, it is legal for one class to use another class that is incompletely defined, there are numerious exampels of this in the question so far.  But there are certain uses that would require that the class be completely defined.  For example if it were to store it as a non-static data member, use it as a base, create a instance of the class, invoke any of its member functions etc.

>> The placement of the source code is
>> vital to such code working
That's always true, just the placement rules may be easier to understand when templates aren't involved.  For example, in your last example here, I can compile it in VC with the Foo<> member functions where you have them and also can compile it if i move the member function to immediately after Foo<> is declared.   i.e. before Bar is declared.  It still works.  I suspect that did not think that it will, right? (you said you thought it would fail, but it does not, even under this contortion.)  What's more--although I know that VC 6 does not handle this right, in fact I don't know of any compiler that does--those member functions definitions don't have to be present in the translation unit (compilation) at all.  According to the standard, with a template class, just like any other class, the translation unit needs to "see" only the declaration of the class.  It does not need to "see" the definition (implimentation) as long as that definition appears in one of the translation units that will be linked together in the final product.  Thus under standard C++, you should be able to get it to compile--though not necessarily link--without those member function defintions anywhere in the code.

Now,when the compiler instanciates a template, it needs to look for errors in how the template parameters are used.  i.e. if template uses a member function of a template parameter and then the template is instanciated with a class that does not define that member function, then an error must be reported.  Now in your example, your getObject() function returns a T object that is created with a default constructor.  This introduces the requirement that the class passed for T have a default.  Now Bar does have a default constructor, but does the compiler "know" that when it defines the Foo<bar> base class?  lets consider a simpler case

template <class T>
class Foo
{
public:
   T & getObject(void);
};

// optional
template <class T> T & Foo<T>::getObject(void) { return T(); } ;

class Bar;

Foo<Bar> b1;

class Bar
{
public:
  Bar(int x = 0);
};

Now Foo<> requires that T have a default cosntructor.  We define a Foo<> for the incompletely defined classe Bar.  If Bar has a default constructor it compiles, if not, it does not compile  (easily changed by adding/remove "= 0" to the constructor parameter.)  But If there is no default constructor when does this error get diagnosed?  Not when b1 is defined.  Because Foo is a completely defined class it is possible to define (well lay out in memory) b1) where b1 is defined, but it is not necessary to check for errors such as the missing constructor at that point.  In fact, not only is it not necessary, it may be impossible to do so.  According to the standard, the line that defines the getObject() member function can be removed from the translation unit.  (In VC the best I can do is move it to the bottom of the source code file, but that still proves the point.)   The point is that this diagnostic must be delayed until the GetObject() member function of Foo is defined and it is possible to create instances of Foo<> before that point.

So where does that get us?  Well in

class Bar : public Foo<Bar>

Foo<> is complete (regardless of the template parameter) so Bar may be derived from any Foo<T>.  However at some point the compiler must check that Foo<Bar> does not use Bar in an inapropriate way.  However that check must be done very late in the compilation/linking procces when the complete definition of Bar is known, so there can be no problem with the compiler trying to test Foo<Bar> for errors before Bar is completely defined.

>> I hope this gives some food for thought
Yes, it did.  And I've decide to leave these questions to Yonat in the future!  She could answer them clearly, susinctly, and I think from memory.  Regretably, she's not a very active expert.
Your construct is legal.
Lets look at the relevant sections in the standard (Public Review/Draft of 2 December 1996)

************************************************************
14.3  Template arguments                                    [temp.arg]
....
10For a template-argument of class type, the template definition has  no
  special  access  rights  to  the  inaccessible members of the template
  argument type.  The name of a template-argument shall be accessible at
  the point where it is used as a template-argument.
************************************************************

A template's type argument *name* must be know at the point of template instantiation. In other words, Bar has to be known as a type name, nothing more

************************************************************
9   Classes                                                    [class]
.....
2 A  class-name is inserted into the scope in which it is declared imme-
  diately after the class-name is seen.  The class-name is also inserted
  into  the scope of the class itself.
************************************************************

Conclusion, the construction you propose:
       template < class T > class Foo { /* .... */ };
       class Bar : public Foo<Bar> {/* ... */ };
is legal

This leaves questions on wether the particular instantition Foo<Bar> is well formed at the point of its instantiation. With regard to Bar, which is not completely defined when Foo<Bar> is instantiated this basically means that Bar may not be a member of Foo<Bar>. You would have trouble with that in a non-template Foo also.

************************************************************
9.2  Class members                                         [class.mem]
.....
2 A class is considered a completely-defined object type (_basic.types_)
  (or  complete  type)  at the closing } of the class-specifier.  Within
  the class member-specification, the  class  is  regarded  as  complete
  within  function  bodies,  default arguments and constructor ctor-ini-
  tializers (including such things in nested classes).  Otherwise it  is
  regarded as incomplete within its own class member-specification.
.....
8 Non-static  (_class.static_)  members  that are class objects shall be
  objects of previously defined classes.   In  particular,  a  class  cl
  shall  not contain an object of class cl, but it can contain a pointer
  or reference to an object of class cl.  When an array is used  as  the
  type of a nonstatic member all dimensions shall be specified.
************************************************************

Regarding instantiation, note that only those member functions that are actually used may be instantiated. The following sections clearly show that
 
************************************************************
14.7.1  Implicit instantiation                             [temp.inst]
1 Unless  a  class template specialization has been explicitly instanti-
  ated (_temp.explicit_) or explicitly  specialized  (_temp.expl.spec_),
  the  class template specialization is implicitly instantiated when the
  specialization is referenced in a context that requires a  completely-
  defined  object  type.   Unless a function template specialization has
  been explicitly instantiated or explicitly specialized,  the  function
  template  specialization  is implicitly instantiated when the special-
  ization is referenced in a context that requires a function definition
  to  exist.   Unless  a static data member template has been explicitly
  instantiated or explicitly specialized, the static  data  member  tem-
  plate  specialization  is implicitly instantiated when the specializa-
  tion is used in a way that requires a definition for the  static  data
  member.
.....
7 An implementation shall not implicitly instantiate  a  function,  non-
  virtual  member  function,  class  or  member  template  that does not
  require instantiation.  It is unspecified whether or not an  implemen-
  tation implicitly instantiates a virtual member function that does not
  require specialization.
************************************************************

A few sections talk aboit the point of instatiation, although that is less relevant to this case.

************************************************************
14.7.1  Implicit instantiation                             [temp.inst]
....
8 Implicitly instantiated class template, function, and static data mem-
  ber specializations are placed in the namespace where the template was
  defined.
9 [Note: _temp.point_ defines the point of instantiation of  a  template
  specialization.  ]

14.6.4.1  Point of instantiation                          [temp.point]
.....
3 Otherwise, the point of instantiation of a function template  special-
  ization immediately follows the namespace scope declaration or defini-
  tion that refers to the specialization.
************************************************************

A final note, in the template construction with Foo<Bar>, there is more room then in the mon-template version:
    class Bar;
    class Foo{public: Bar getObject(){ return Bar(); }};
    class Bar : public Foo{};
Foo may not refer to any members of Bar, so getObject() is ill formed. The template version
    template <class T> class Foo{public: T getObject(){ return T(); }};
    class Bar : public Foo<Bar>{};
however is well formed since with type dependent contsructs are name-look-up occurs when the template (member) is instantiated (see above)

************************************************************
14.6.2  Dependent names                                     [temp.dep]

1 Inside a template, some constructs have  semantics  which  may  differ
  from  one  instantiation  to another.  Such a construct depends on the
  template argument.  In particular, types and expressions may depend on
  the  type  and or value of templates arguments and this determines the
  context for name lookup for certain names.  Expressions may  be  type-
  dependent  (on the type of a template argument) or value-dependent (on
  the value of a non-type template argument).  In an expression  of  the
  form:

        postfix-expression ( expression-listopt )
  where the postfix-expression is an identifier, the identifer denotes a
  dependent name if and only if any of the expressions  in  the  expres-
  sion-list  is  a  type-dependent  expression (_temp.dep.expr_).  If an
  operand of an operator is a type-dependent  expression,  the  operator
  also  denotes a dependent name.  Such names are unbound and are looked
  up at the point of the template instantiation (_temp.point_)  in  both
  the context of the template definition and the context of the point of
  instantiation.
************************************************************

|>> The placement of the source code is
|>> vital to such code working
|That's always true, just the placement rules may be
|easier to understand when templates aren't involved.  
Nope. Placement of your templates member implementation is not an issue, as long as they appear before the instatiation, of the member. So yes (indeed surprisingly, VC is right), Foo<>'s member functions may refer to Bar's members!

>> Placement of your templates member
>> implementation is not an issue
That's the point I made.  As I said, it doesn't even have to be in the translation unit at all--according to the standard, not yet true for VC.
You did indeed, it just appeared differently at the start of that paragraph.
In other words..."basically your "answer" is a mess but I'm too polite to say that."    Yeah I bit off more that I could chew on this one.
:)
Well, blame me too. I should have read it again before snipping a piece from it and shooting that off.
There is just too much complicated text in this thread too be able to maintain a clear overview of what was written.
Any comment on my conclusion that the proposed construct is legal?
Avatar of barryc

ASKER

From what I gather, a template can do almost anything it wants with its templated "class type" parameter since the definition of the template does not even have to be in the translation unit.  Hence, a class CAN derive from a template on itself and that template can use that incomplete class in any way, since the actual checking cannot even be done until the class is completely defined.  

It seems like a loophole in the language, or perhaps an intended, but obscure, mechanism.  My only worry is at the linking stage.  I tried creating an example that would fail to link, but I couldn't get it to fail.  From this I feel comfortable using Foo<Bar> as a base class for Bar, but I'm not sure I feel confident that it will always and portably link in the case where Foo<> refers directly to members of the templated type.

If I were to make a hypothesis, I would say that the code will actually link under _all_ compilers.  I say this because template classes and functions almost always refer to members of classes that don't yet exist. Because of that, the linker propbably has to link all generated template code against all non-template code FIRST, and then keep generated template functions and members visible at all stages of linking.

Thanks to nietod and KangaRoo for all the help.  I hope researching this point helps you both out too because I know you both devoted alot of time to this.

As a final note, if somebody could prove or disprove what sort of "self-template-base-clases" would succeed or fail in linking, I would be very interested to hear the answer.
Avatar of barryc

ASKER

I will post a 300 point question for KangaRoo to answer so that he may claim some points for his/her help on this issue.  It know it wasn't an easy one.
>> Hence, a class CAN derive from a template on itself and
>> that template can use that incomplete class in any way

It can not have data members of that incomplete class.

>> It seems like a loophole in the language, or perhaps
>> an intended, but obscure, mechanism.
Certainly obscure, as is often the case with templates :-\


It did add to my C++ knowledge and there is yet more to be known, especially about templates. I did have to read the section on Classes and part of the section on templates and believe me, reading the standard is not my everyday hobby. Nietod's comment that yonat appears to know section by heart was quite a shock ;)

Good templuck.
>> Nietod's comment that yonat appears to know
>> section by heart was quite a shock
An exageration to be sure, but she is pretty amazing.  OO theory in particular but C++ minutia as well.

>> a template can do almost anything it wants with
>> its templated "class type" parameter since the
>> definition of the template does not even have to
>> be in the translation unit
Actually this is no really more freedom than an non-template class.  A non-template class must have a definition (implimentation), but that does not have to be in the translation unit.  The same things is true for non-template functions, that is, there must be a defintion somewhere, but it does not have to appear in every translation unit that uses it.  Thus according to the standard template classes and template functions aren't supposed to add the restriction that the definitions must appear in the translation units that use the templates.  However implimenting this in the compiler is extremely difficult, so currently many compilers do add the restriction for templates.

>> but I'm not sure I feel confident that it will always
>> and portably link in the case where Foo<> refers
>> directly to members of the templated type
While I can't see it spelled out susinctly, i am very satisfied that this will work in standard C++ and on most modern compilers, and more compilers as time goes on.  You should not have problems unless you do something that would be illegal, like have Foo store a Bar data member.
>> where Foo<> refers directly to members of the templated type

Section 14.6.2 deals with that. Resuming it, anything in the template that depends on the template's argument is evaluated when that dependant is instantiated. You don't have to worry about that.
As for the current need to include the templates implementation in every translation unit, you can do as with 'normal' code and put in a .cpp implementation. The include the implementation in the templates header:

// header
#ifndef MYTEMPLATE_H
#define MYTEMPLATE_H
// Stuff
#include <mytemplate.cpp>
#endif

// implementation
#ifndef MYTEMPLATE_H
#include <mytemplate.h>
#endif
// implementation
I think its getting off topic, but if you do that you probably want to include a condition that prevents the include on a fully-template compiler, because this is tecnhically an error (multiple definitions.)  So you would have

#ifndef MYTEMPLATE_H
#define MYTEMPLATE_H
// Stuff
#ifndef TEMPLATE_COMPLIANT
#include <mytemplate.cpp>
#endif
#endif
Avatar of barryc

ASKER

>>> where Foo<> refers directly to members of the templated type

> Section 14.6.2 deals with that. Resuming it, anything in the template that
> depends on the template's argument is evaluated when that dependant is
> instantiated. You don't have to worry about that.

Okay, so now I'm confused.  I'm gonna post some examples and could someone
let me know which of these are legal and which are illegal.  I'll put what I
think after each example....

================================================================================
// EXAMPLE A

template <class T>
class Foo {
public:
  friend T;

private:
  inline Foo(void) { }
  inline Foo(const Foo& f) { }
  virtual ~Foo(void) { }
  inline Foo& operator=(const Foo&) { return *this; }
};

class Bar : public virtual Foo<Bar> {
public:
  inline Bar(void) : Foo<Bar>() { }
  inline Bar(const Bar& b) : Foo<Bar>(b) { }
  virtual ~Bar(void) { }
  inline Bar& operator=(const Bar& b) { Foo<Bar>::operator=(b); return *this; }
};

int main(int argc, const char* argv[]) {
  Bar b;

  return 0;
}
================================================================================
I think example A would be legal.

================================================================================
// EXAMPLE B

template <class T>
class Foo {
public:
  inline Foo(void) { }
  inline Foo(const Foo& f) { }
  virtual ~Foo(void) { }
  inline Foo& operator=(const Foo&) { return *this; }

  virtual T* getNewObject(void) const = 0;
};

class Bar : public Foo<Bar> {
public:
  inline Bar(void) : Foo<Bar>() { }
  inline Bar(const Bar& b) : Foo<Bar>(b) { }
  virtual ~Bar(void) { }
  inline Bar& operator=(const Bar& b) { Foo<Bar>::operator=(b); return *this; }

  virtual Bar* getNewObject(void) const { return new Bar(); }
};

int main(int argc, const char* argv[]) {
  Bar b;
  Foo<Bar>* fp = &b;                    // refer to b as base type
  Bar* bp = fp->getNewObject(); // call virtual method through Foo ptr

  delete bp;
  return 0;
}
================================================================================
I think example B would be legal.

================================================================================
// EXAMPLE C

template <class T>
class Foo {
public:
  inline Foo(void) { }
  inline Foo(const Foo& f) { }
  virtual ~Foo(void) { }
  inline Foo& operator=(const Foo&) { return *this; }

  T* getNewObject(void);
};

class Bar : public Foo<Bar> {
public:
  inline Bar(void) : Foo<Bar>() { }
  inline Bar(const Bar& b) : Foo<Bar>(b) { }
  virtual ~Bar(void) { }
  inline Bar& operator=(const Bar& b) { Foo<Bar>::operator=(b); return *this; }
};

// define template function AFTER Bar derives from Foo<Bar>
template <class T> T* Foo<T>::getNewObject(void) {
  return new T();
}

int main(int argc, const char* argv)
{
  Bar b;
  Bar* bp = b.getNewObject();

  delete bp;
  return 0;
}
================================================================================
I think example C would be legal.

================================================================================
// EXAMPLE D

template <class T>
class Foo {
public:
  inline Foo(void) { }
  inline Foo(const Foo& f) { }
  virtual ~Foo(void) { }
  inline Foo& operator=(const Foo&) { return *this; }

  T* getNewObject(void);
};

// define template function BEFORE Bar derives from Foo<Bar>
template <class T> T* Foo<T>::getNewObject(void) {
  return new T();
}

class Bar : public Foo<Bar> {
public:
  inline Bar(void) : Foo<Bar>() { }
  inline Bar(const Bar& b) : Foo<Bar>(b) { }
  virtual ~Bar(void) { }
  inline Bar& operator=(const Bar& b) { Foo<Bar>::operator=(b); return *this; }
};

int main(int argc, const char* argv)
{
  Bar b;
  Bar* bp = b.getNewObject();

  delete bp;
  return 0;
}
================================================================================
I think example D would be legal.

================================================================================
// EXAMPLE E

template <class T>
class Foo {
public:
  inline Foo(void) { }
  inline Foo(const Foo& f) { }
  virtual ~Foo(void) { }
  inline Foo& operator=(const Foo&) { return *this; }

  T getObject(void);
};

class Bar : public Foo<Bar> {
public:
  inline Bar(void) : Foo<Bar>() { }
  inline Bar(const Bar& b) : Foo<Bar>(b) { }
  virtual ~Bar(void) { }
  inline Bar& operator=(const Bar& b) { Foo<Bar>::operator=(b); return *this; }
};

// define template function AFTER Bar derives from Foo<Bar>
template <class T> T Foo<T>::getObject(void) {
  return T();
}

int main(int argc, const char* argv)
{
  Bar b1;
  Bar b2 = b1.getObject();

  return 0;
}
================================================================================
I'm not sure if example E is legal or not.

================================================================================
// EXAMPLE F

template <class T>
class Foo {
public:
  inline Foo(void) { }
  inline Foo(const Foo& f) { }
  virtual ~Foo(void) { }
  inline Foo& operator=(const Foo&) { return *this; }

  static T object;  // make a static data member
};

class Bar : public Foo<Bar> {
public:
  inline Bar(void) : Foo<Bar>() { }
  inline Bar(const Bar& b) : Foo<Bar>(b) { }
  virtual ~Bar(void) { }
  inline Bar& operator=(const Bar& b) { Foo<Bar>::operator=(b); return *this; }
};

// define template data member AFTER Bar derives from Foo<Bar>
template <class T> T Foo<T>::object;

int main(int argc, const char* argv)
{
  Bar b;

  return 0;
}
================================================================================
I'm not sure if example F is legal or not.

================================================================================
// EXAMPLE G

template <class T>
class Foo {
public:
  inline Foo(void) { }
  inline Foo(const Foo& f) { }
  virtual ~Foo(void) { }
  inline Foo& operator=(const Foo&) { return *this; }

  static T object;  // make a static data member
};

// define template data member BEFORE Bar derives from Foo<Bar>
template <class T> T Foo<T>::object;

class Bar : public Foo<Bar> {
public:
  inline Bar(void) : Foo<Bar>() { }
  inline Bar(const Bar& b) : Foo<Bar>(b) { }
  virtual ~Bar(void) { }
  inline Bar& operator=(const Bar& b) { Foo<Bar>::operator=(b); return *this; }
};

int main(int argc, const char* argv)
{
  Bar b;

  return 0;
}
================================================================================
I'm not sure if example G is legal or not.

================================================================================
// EXAMPLE H

template <class T>
class Foo {
public:
  inline Foo(void) { }
  inline Foo(const Foo& f) { }
  virtual ~Foo(void) { }
  inline Foo& operator=(const Foo&) { return *this; }

private:
  T member;  // make a non-static data member
};

class Bar : public Foo<Bar> {
public:
  inline Bar(void) : Foo<Bar>() { }
  inline Bar(const Bar& b) : Foo<Bar>(b) { }
  virtual ~Bar(void) { }
  inline Bar& operator=(const Bar& b) { Foo<Bar>::operator=(b); return *this; }
};

int main(int argc, const char* argv)
{
  Bar b;

  return 0;
}
================================================================================
Example H is DEFINITELY illegal since "Bar" is incomplete and cannot be used as
a data member.  Even if it did compile, it would go into an infinite recursive
loop when constructing "b" since b would construct Foo<Bar> which would
construct an object of type Bar which would need to construct another Foo<Bar>
and so on...

Well, that's enough examples.  If someone could state which are legal and which
are illegal (and include a reason why they are illegal) that would be great.

I just want to make sure that we're all on the same sheet of music.

Thanks!
Avatar of barryc

ASKER

Please note that in example E that the return type of "getObject()" is not a pointer, but an actual object.
Avatar of barryc

ASKER

Just for clarification of what each example is trying to do....

Example A: Uses incomplete template type as a friend.

Example B: Uses incomplete template type as a pointer.

Example C: Uses incomplete template type as a pointer and defines a template method which accesses the default constructor and new operator of the template type.  The template method is defined AFTER the template type class declaration is complete.

Example D: Uses incomplete template type as a pointer and defines a template method which accesses the default constructor and new operator of the template type.  The template method is defined BEFORE the template type class declaration is complete.

Example E: Uses incomplete template type as a return value (not a pointer) and defines a template method which accesses the default constructor of the template type.  The template method is defined AFTER the template type class declaration is complete.

MISSING EXAMPLE (E2--see below): Uses incomplete template type as a return value (not a pointer) and defines a template method which accesses the default constructor of the template type.  The template method is defined BEFORE the template type class declaration is complete.

================================================================================
// EXAMPLE E2

template <class T>
class Foo {
public:
  inline Foo(void) { }
  inline Foo(const Foo& f) { }
  virtual ~Foo(void) { }
  inline Foo& operator=(const Foo&) { return *this; }

  T getObject(void);  // return an actual object
};

// define template function BEFORE Bar derives from Foo<Bar>
template <class T> T Foo<T>::getObject(void) {
  return T();
}

class Bar : public Foo<Bar> {
public:
  inline Bar(void) : Foo<Bar>() { }
  inline Bar(const Bar& b) : Foo<Bar>(b) { }
  virtual ~Bar(void) { }
  inline Bar& operator=(const Bar& b) { Foo<Bar>::operator=(b); return *this; }
};

int main(int argc, const char* argv)
{
  Bar b1;
  Bar b2 = b1.getObject();

  return 0;
}
================================================================================

Example F: Uses the incomplete template type as a static data member and defines the static data member AFTER the template type class declaration is complete.

Example G: Uses the incomplete template type as a static data member and defines the static data member BEFORE the template type class declaration is complete.

Example H: Uses the incomplete template type as a non-static data member and implicitly refers to the default constructor of the incomplete template type.
>> Example A: Uses incomplete template type as a friend.
Legal, but the standard would require that the friend class be declared before the friend statement.   Most compilers do not require this yet, it was a recent change.

>> Example B: Uses incomplete template type as a pointer.
Legal.

>>Example C: Uses incomplete template type as a pointer
>> and defines a template method which accesses the default
>> constructor and new operator of the template type.  The
>> template method is defined AFTER the template type class
>> declaration is complete.
Legal

>> Example D: Uses incomplete template type as a pointer
>> and defines a template method which accesses the default
>> constructor and new operator of the template type.  The
>> template method is defined BEFORE the template type class
>> declaration is complete.
Legal

>> Example E: Uses incomplete template type as a return value
>> (not a pointer) and defines a template method which
>> accesses the default constructor of the template type.  The
>> template method is defined AFTER the template type class
>> declaration is complete.
Legal

>> MISSING EXAMPLE (E2--see below): Uses incomplete
>> template type as a return value (not a pointer) and defines
>>a template method which accesses the default constructor
>> of the template type.  The template method is defined
>> BEFORE the template type class declaration is complete.
Legal

>> Example F: Uses the incomplete template type as a static
>> data member and defines the static data member AFTER
>> the template type class declaration is complete.
I THINK this is legal

>> Example G: Uses the incomplete template type as a static
>> data member and defines the static data member BEFORE
>> the template type class declaration is complete.
This would be the same as F.  I think both are legal.

>> Example H: Uses the incomplete template type as a
>> non-static data member and implicitly refers to the default
>> constructor
Illegal.  I think modula-2 is the only language that "allows" recursive data structures and I don't think any implimentation has actually every supported them.
           of the incomplete template type.
Avatar of barryc

ASKER

nietod: For example A, you say that the friend class needs to be declared before it is made a friend.  The point at which the class is declared is when the class declaration begins, right?  Or do I need to first forward declare the class?

For example, for this template class:
=============================================
template <class T> class Foo {
public:
  friend T;
};
=============================================
Is this okay:
=============================================
class Bar /* CLASS IS NOW DECLARED */
 :  virtual public Foo<Bar> { /* ... */ };
=============================================
??? OR do I  need to do something like:
=============================================
class Bar; // Make sure class is declared
class Bar : virtual public Foo<Bar> { /* ... */ };
=============================================

KangRoo preivously posted:

************************************************************
9   Classes                                          [class]
......
2 A  class-name is inserted into the scope in which it is
  declared immediately after the class-name is seen.  The
  class-name is also inserted into the scope of the class
  itself.
************************************************************

If this is true in the final standard then doesn't that mean that the class is declared prior to it being made a friend in example A.  And thus, example A is completely legal?

Thanks again!
I think the draft is pretty clear about it, it uses 'seen' so I think Ex A is completely legal.

Use of static members of incomplete type is allowed according to the draft (not with gcc though)

  9.4.2  Static data members                         [class.static.data]

....

2 The declaration of a static data member in its class definition is not
  a  definition and may be of an incomplete type other than cv-qualified
  void.  A definition shall be provided for the static data member if it
  is used (_basic.def.odr_) in the program. ...
Oeps, gcc does allow the
  static T object;
but the initialization can only appear after Bar is complete.
According to gcc F is legal and G is illegal.
According to the darft, 14.7.1 secton 7 it is legal, since Foo<>::object is never used, it should never be instantiated.

7 An implementation shall not implicitly instantiate  a  function,  non-
                               virtual  member  function,  class  or  member  template  that does not
                               require instantiation.  It is unspecified whether or not an  implemen-
                               tation implicitly instantiates a virtual member function that does not
                               require specialization.
In exampel A

template <class T>
class Foo {
public:
friend T; // This will be an error if T is not defined at the
 ///point of instanciation.  (According to the final standard,
 // not originally.  Originally a friend declaration was a forward
 // declaration.  Now it is not.
};

You could use

template <class T>
class Foo {
public:
class T; // This fixes the problem.
friend T;
};

to fix the problem.

*****************************

>> According to gcc F is legal and G is illegal.
Does gcc allow you to compile templates whent he template's definitions is not "visible" to the translation unit?  Compilers that don't yet support that (nearly all) might not be able to support G.

****************************

>> According to the darft, 14.7.1 secton 7 it is legal, since
>> Foo<>::object is never used, it should never be
>> instantiated.

I don't think you applied that rule correctly.  It does not say that data members, or even static data members, won't be instanciated..  

I think the static should be created and on a fully compliant compiler it will d so without problems.
Possibly yes. Darn, it so hard to find what you need in that document :(
I think forward declaration of T in the template itself is not a good idea, possibly even illegal. I could find the restriction that the friend must be a completely defined class either, so I don't think declaring T as friend is illegal. When the template is instantiated, at least the name of the specialization argument (Bar) exists.
That came for an Allan D. Clarke C++ tip.  however on rereading the tip, it specifically says that this is true for friend _functions_.  i.e. declaring a function as a friend does not forward declare the function.  It does not mention classes in this instance.  
Avatar of barryc

ASKER

So, example A _is_ legal and the forward declaration rule does not apply.  That rule would only apply to friend _functions_.  That "friend function" rule that nietod references would mean that a global function must have a prototype before it can be made a friend of a class, whereas making it a friend previously acted as the prototype for the function as well.  For example, the legal (ANSI) way to make a global function into a friend would be:

================================================

void foo(int, float);  // prototype the function

class A {
public:
  /* . . . */
  friend void foo(int float); // make it a friend

};

// define the function
void foo(int x, float y) {
  /* . . . }
}

================================================

I guess that is a little off track from the subject of this question, but I just want to make sure of what nietod was saying.

BTW: We should look into a writing an online "chat whiteboard" in Java for handling discussions on questions like this. =)
There is no existing chat channel on the C++ standard, how can that be???
>> example A _is_ legal and the forward declaration
>> rule does not apply
From the tip it is not clear if it applies or not for a class.  I'm not sure what the final standard says on it as I don't have a copy and the tip suggests that this was a last minute change.  (The draft standard I use is pretty close, but not perfect.)

The example is right for the declaring the function.

>> We should look into a writing an online "chat whiteboard"
What about alt.languages.C++ or whatever it is called?  (where Bjarne Stoustrup and the other C++ bigwigs hand out.)
Avatar of barryc

ASKER

Oh, I just wanted a whiteboard so we can draw pictures of what we're talking about.  I don't know about the rest of you, but most of my brainstorming at work occurs on the whiteboard.
If forward declarartion is no longer possible (or possible) a whole lot of designs are going to be broken, which is a few steps beyond the 'for' thing.
If the subject of a friend declaration has to be a completely defined type, mutual friends can never exist. It will also be impossible for a befriended class to have member objects of the befriending class. This is not a last minute change.
Avatar of barryc

ASKER

I'm pretty sure that tip must only apply to friend functions and not friend classes.  Making another class a friend does not require any knowledge of that classes methods, it's only provides access to that class so it can see your classes private methods.  I don't think nietod is suggesting that friend classes need to be fully defined, I think he's trying to say that they just need to be forward declared.
No, I'm sure the class doesn't doesn't need to be fully declared, it just needs to be delcared as a class.  (if the tip applies to classes at all.)  The point of the tip was that a friend declaration, is not a forward declaration--at least for a function.   From the tip

void f();

class C {
   friend void f(); // The f above is a friend
   friend void g(); // Some unknown g is a friend if it gets declared.
   };
void h1 () {
   g();  // error, there is no g in scope
   }
void g(); // Here is that g and it is a friend
void h2 () {
   g();  // ok, calls the friend
   }

Like that. Seems ok.