[comp.lang.c++] static members

rpj@redcloud.cad.mcc.com (Rich Johns) (01/07/89)

In Stroustroup on page 275, section 8.5.1 it states:

   "No initializer can be specified for a static member, 
    and it cannot be of a class with a constructor."

This could mean two things(at least):

1) if a class has a constructor, that class may NOT have 
   static members.

2) You cannot have a static data member which needs a 
   constructor.

After discussing this with someone, I am inclined to 
think number 2 is correct. As such are the following
assumptions correct:

 a) a static data member is allowed, as long as:
    1) the member does not require a constructor.
    2) the member is private

 b) a static const data member  is allowed, as long as:
    1) the member does not require a constructor.
    2) the member is private

 c) a static const data member CAN have an initial value.
 
 d) a static function member is not allowed.



Rich Johns, MCC CAD Program | 3500 W. Balcones Center Dr., Austin, TX 78759
ARPA: johns@mcc.com         | Phone: [512] 338-3714
UUCP: {uunet,harvard,gatech,pyramid}!cs.utexas.edu!milano!cadillac!johns

mball@cod.NOSC.MIL (Michael S. Ball) (01/07/89)

In article <701@cadillac.CAD.MCC.COM> rpj%cad@MCC.COM (Rich Johns) writes:
>
>In Stroustroup on page 275, section 8.5.1 it states:
>
>   "No initializer can be specified for a static member, 
>    and it cannot be of a class with a constructor."
>
>2) You cannot have a static data member which needs a 
>   constructor.

This is the correct interpretation.  A type which has a constructor
must be initialized, and there is no defining point for a static
member where the compiler could put the initializer.

> a) a static data member is allowed, as long as:
>    1) the member does not require a constructor.
>    2) the member is private

There is no restriction on access.  It can be public, protected or private.

> b) a static const data member  is allowed, as long as:

Hard to see what use it is, except as a source of constant zero.
usually constants must be initialized.

> c) a static const data member CAN have an initial value.

Only the default value of zero.

> d) a static function member is not allowed.

True.

All of these answers apply to existing implementations of the language
(cfront 1.2 ore equal).  There were discussions of ways to change them
at the USENIX workshop, so they should not be considered the last word.

Mike Ball
TauMetric Corporation

ark@alice.UUCP (Andrew Koenig) (01/07/89)

In article <701@cadillac.CAD.MCC.COM>, rpj@redcloud.cad.mcc.com (Rich Johns) writes:

> In Stroustroup on page 275, section 8.5.1 it states:
> 
>    "No initializer can be specified for a static member, 
>     and it cannot be of a class with a constructor."

> This could mean two things(at least):

> 1) if a class has a constructor, that class may NOT have 
>    static members.

> 2) You cannot have a static data member which needs a 
>    constructor.

(2) is correct.  The reason for this is that the definition
of a static data member appears as part of the class definition,
which is generally replicated in several modules.  If the
member requires a constructor, that constructor would effectively
be called in each module, thus initializing the member many times.

> After discussing this with someone, I am inclined to 
> think number 2 is correct. As such are the following
> assumptions correct:

>  a) a static data member is allowed, as long as:
>     1) the member does not require a constructor.
>     2) the member is private

It doesn't have to be private.

>  b) a static const data member  is allowed, as long as:
>     1) the member does not require a constructor.
>     2) the member is private

It doesn't have to be private.

>  c) a static const data member CAN have an initial value.

Yes indeed.

>  d) a static function member is not allowed.

It would make sense, wouldn't it?

	class Widget {
		static int n;
	public:
		Widget() { n++; /* other stuff */ }
		~Widget() { n--; /* other stuff */ }
		static int count() { return n; }
		/* other stuff */
	};

This is an example of a Widget class that, among other things,
counts how many Widgets there are in the universe.  Thus if I say

	Widget x;

	cout << x.count() << "\n";

it prints 1 (unless there are other Widgets I didn't mention).
Of course if there aren't any Widgets at all I can't use this
notation to count them, but I should be able to call Widget::count.

Anyway, it's an interesting idea, but not presently implemented.
-- 
				--Andrew Koenig
				  ark@europa.att.com

schmidt@glacier.ics.uci.edu (Doug Schmidt) (01/08/89)

In article <1354@cod.NOSC.MIL> mball@cod.nosc.mil.UUCP (Michael S. Ball) writes:
>In article <701@cadillac.CAD.MCC.COM> rpj%cad@MCC.COM (Rich Johns) writes:

>> c) a static const data member CAN have an initial value.
>
>Only the default value of zero.
>

I was under the impression ( after reading some postings here and from
experimenting with cfront 1.2 ) that a static const data member CAN
have an initial value.

As proof by existence ;-), try this out on cfront 1.2.1:

----------------------------------------
class foo {
public:
   static const int i = 100;
   int a [ i ];

   foo ( ) { ; }
};

main ( ) {
   foo f;
   printf ( "elements in f.a = %d\n", sizeof f.a / sizeof *f.a);
   // should print out 100
}
----------------------------------------
>
>All of these answers apply to existing implementations of the language
>(cfront 1.2 ore equal).  There were discussions of ways to change them
>at the USENIX workshop, so they should not be considered the last word.
>

I hope that this working example is not just a fluke, but is rather
either a part ( or soon to be a part ) of the official definition
of the language.

Any comments?

Doug
--
schmidt@ics.uci.edu (ARPA) |   Per me si va nella citta' dolente.
office: (714) 856-4043     |   Per me si va nell'eterno dolore.
                           |   Per me si va tra la perduta gente.
                           |   Lasciate ogni speranza o voi ch'entrate.

pcg@aber-cs.UUCP (Piercarlo Grandi) (01/08/89)

In article <8695@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes:

    In article <701@cadillac.CAD.MCC.COM>, rpj@redcloud.cad.mcc.com (Rich
    Johns) writes:

	[ ... several sensible questions about static class members ... ]

    [ ... equally sensible replies to such questions, with an interesting
    comment on static function members ... ]

Frankly I find this whole business of static members difficulties a bit
disappointing. I think that initialized static members, initialized static
const members and such are useuful concepts and ought to be allowed.

I am aware, as it has been correctly observed, that initializing static
members runs the risk, in a straightforward implementation, to have multiple
initializations, because the class definition is included potentially
several times in several source files.

I can think of three solutions to this:

    [1] consider static members as de facto global variables (e.g.  regard
    "C::m" as a kind of "C_m") and allow initialization of the member outside
    the class, away from the definition:

	class C { ... static int m; ... }; int C::m = 1;

    which resembles the current allowed syntax for defining member functions
    away from their declaration, outside the class definition. Note that the
    rationale for doing this in the case of procedure members is exactly
    equivalent to the rationale for allowing this for data members (avoiding
    replication).

    [2] sidestep the problem entirely, by using an import or export syntax
    and mechanism . This has several other advantages, some people (including
    myself) reckon.

    [3] add yet another kludgey wart to "bunch", or "collect", or whatever
    tools is used to reconcile the ideal of C++ global objects with the
    reality of existing linkers.

In my reckoning, solution [1] does not address very well the case of static
members of a type with constructors, solution [3] is inelegant, as all
compromises are, and solution [2] works well, and only impacts C++ compilers,
not linkers.

There has been a discussion in this newsgroup in the past on the relative
merits of an import/export mechanism to replace the current reliance on
#include, and it was not very conclusive. Can I now add that the static
members problem is one fair example where the wholesale replication of text
implied by #include causes problems?


As to the static procedure idea, this adds a new dimension to the
discussion.  C++ does not have metaclasses. It also does not have a
proper way of defining class attributes, as opposed to instance
attributes. The static function idea to me shows that a good way to add
them to the language would be to say "static members of a class
definition are members of the class object, not of any instance object
of that class". A possible consequence could be to define "C::m" to be
the syntax used to access members of the class object, and "C.m" to
access members of an instance object.

Consider the example by Andrew Koenig:

	class Widget {
		static int n;
	public:
		Widget() { n++; /* other stuff */ }
		~Widget() { n--; /* other stuff */ }
		static int count() { return n; }
		/* other stuff */
	};

	[ ... ]

    Of course if there aren't any Widgets at all I can't use this notation to
    count them, but I should be able to call Widget::count.

It seems to me fairly clear that "count" is a class/factory method, in
Smalltalk/Objective C terminology.  Now class methods ARE useful in
Smalltalk, as factory methods are useful in Objective C.

Under this interpretation, the problems with static data members acquire a
new dimension: they really are members of the class object, and the
difficulties with them are merely a reflection of the fact that in C++ there
is no explicit or clear notion and support (yet) of a class object or
metaclasses.

This said I would not however explicitly introduce the notion of metaclass
in C++, because it seems to me that it does not belong to a language where
the environment is not expected to contain much (what I mean is that
certainly a C++ compiler will implement metaclasses -- the symbol table entry
for a class -- but then probably this compile time structure should not
become in toto a runtime entity as well).

It seems that extending Andrew Koenig's idea to say that C++ acquire a
mechanism like static to achive something similar to the distinction between
+/- members in Objective C is not, at first sight, a terribly bad
suggestion, and would be sufficient.
-- 
Piercarlo "Peter" Grandi	   | ARPA: pcg%cs.aber.ac.uk@nss.cs.ucl.ac.uk
Dept of CS, UCW Aberystwyth	   | UUCP: ...!mcvax!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk

pcg@aber-cs.UUCP (Piercarlo Grandi) (01/08/89)

[this is a repost of an article that did not get out a week ago because of
net problems -- I think it is still useful]

In article <8695@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes:

    In article <701@cadillac.CAD.MCC.COM>, rpj@redcloud.cad.mcc.com (Rich
    Johns) writes:

	[ ... several sensible questions about static class members ... ]

    [ ... equally sensible replies to such questions, with an interesting
    comment on static function members ... ]

Frankly I find this whole business of static members difficulties a bit
disappointing. I think that initialized static members, initialized static
const members and such are useuful concepts and ought to be allowed.

I know that initializing static members, as it has been correctly observed,
runs the risk, in a straightforward implementation, to have multiple
initializations, because the class definition is included potentially several
times in several source files.

I can think of three solutions to this (some other proposal was voiced in
some C++ conference but I don't yet have the proceedings to look it up):

    [1] consider static members as de facto global variables (e.g.  regard
    "C::m" as a kind of "C_m") and allow initialization of the member outside
    the class, away from the definition:

	class C { ... static int m; ... }; int C::m = 1;

    which resembles the current allowed syntax for defining member functions
    away from their declaration, outside the class definition. Note that the
    rationale for doing this in the case of procedure members is exactly
    equivalent to the rationale for allowing this for data members (avoiding
    replication).

    [2] sidestep the problem entirely, by using an import or export syntax
    and mechanism . This has several other advantages, some people (including
    myself) reckon.

    [3] add yet another kludgey wart to "bunch", or "collect", or whatever
    tools is used to reconcile the ideal of C++ global objects with the
    reality of existing linkers.

In my reckoning, solution [1] does not address very well the case of static
members of a type with constructors, solution [3] is inelegant, as all
compromises are, and solution [2] works well, and only impacts C++ compilers,
not linkers.

There has been a discussion in this newsgroup in the past on the relative
merits of an import/export mechanism to replace the current reliance on
#include, and it was not very conclusive. Can I now add that the static
members problem is one fair example where the wholesale replication of text
implied by #include causes problems?


As to the static procedure idea, this adds a new dimension to the discussion.
C++ does not have metaclasses. It also does not have a proper way of defining
class attributes, as opposed to instance attributes. The static function idea
to me shows that a good way to add them to the language would be to say "static
members of a class definition are members of the class object, not of any
instance object of that class". A possible consequence could be to define
"C::m" to be the syntax used to access members of the class object, and "C.m"
to access members of an instance object.

Consider the example by Andrew Koenig:

	class Widget {
		static int n;
	public:
		Widget() { n++; /* other stuff */ }
		~Widget() { n--; /* other stuff */ }
		static int count() { return n; }
		/* other stuff */
	};

	[ ... ]

    Of course if there aren't any Widgets at all I can't use this notation to
    count them, but I should be able to call Widget::count.

It seems to me fairly clear that "count" is a class/factory method, in
Smalltalk/Objective C terminology.  Now class methods ARE useful in
Smalltalk, as factory methods are useful in Objective C.

Under this interpretation, the problems with static data members acquire a
new dimension: they really are members of the class object, and the
difficulties with them are merely a reflection of the fact that in C++ there
is no explicit or clear notion and support (yet) of a class object or
metaclasses.

This said I would not however explicitly introduce the notion of metaclass
in C++, because it seems to me that it does not belong to a language where
the environment is not expected to contain much (what I mean is that
certainly a C++ compiler will implement metaclasses -- the symbol table entry
for a class -- but then probably this compile time structure should not
become in toto a runtime entity as well).

It seems that extending Andrew Koenig's idea to say that C++ acquire a
mechanism like static to achive something similar to the distinction between
+/- members in Objective C is not, at first sight, a terribly bad
suggestion, and would be sufficient.
-- 
Piercarlo "Peter" Grandi	   | ARPA: pcg%cs.aber.ac.uk@nss.cs.ucl.ac.uk
Dept of CS, UCW Aberystwyth	   | UUCP: ...!mcvax!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk
-- 
Piercarlo "Peter" Grandi	   | ARPA: pcg%cs.aber.ac.uk@nss.cs.ucl.ac.uk
Dept of CS, UCW Aberystwyth	   | UUCP: ...!mcvax!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk

mball@cod.NOSC.MIL (Michael S. Ball) (01/09/89)

In article <3381@paris.ics.uci.edu> Doug Schmidt <schmidt@glacier.ics.uci.edu> writes:
>I hope that this working example is not just a fluke, but is rather
>either a part ( or soon to be a part ) of the official definition
>of the language.

Sorry, but it is a fluke.  Cfront may let it through (all programs have
bugs) but many C compilers won't (or at least their linkers won't.)
A variable initialized in more than one file is not legal in C, and
many systems simply bomb on linking, since the compiler can't tell.
The C++ language definite explicitly disallows it.

The syntax for the feature that was proposed at the USENIX workshop
was nothing like this.  I'm not going to speculate further on what
might get into the language.  There are enough rumors and misconceptions
flying about as it is.

-Mike Ball-
TauMetric Corporation
(619)275-6381

jima@hplsla.HP.COM (Jim Adcock) (01/10/89)

I guess I'm confused by some of this conversation.
Are some people missing the fact that static members 
are shared by all objects of a class?

bs@alice.UUCP (Bjarne Stroustrup) (01/16/89)

I'm sorry I haven't commented on the discussion on static members
before. I have been busy. Release 2.0 will provide two new features
related to static members:
	(1) static member functions, and
	(2) initialization of static members
both have been mentioned in comp.lang.c++ before. I believe that
the idea of static member functions originate with Jonathan Shopiro.
Unfortunately, I don't know who first proposed the way of for initializing
static members we adopted; Andrew Koenig brought it to my attention, but
he couldn't remember who gave it to him. Both ideas were presented and
discussed at the implementors workshop at the Denver C++ conference.

1: static member functions

	The reason for static members is to reduce the number of global names,
	to make it obvious which class objects belong, and to be able to apply
	the access control rules to names of objects and values that are not
	replicated one per object. This is a boon for library providers in that
	it avoids polluting the global name space and thereby allow easier
	writing of library code and safer use of multiple libraries.

	It was observed that these reasons applied for functions as well as
	for objects and the absence of static functions has been commented
	on repeatedly over the years. It was observed that MOST of the names
	a library provider wants local are in fact function names. it was
	observed that nonportable code, such as
		((X*)0)->f();
	was used to simulate static member functions (this ``trick'' is a
	time bomb because it will fail under some implementations of dynamic
	linking).

	We now have a facility that is logically equivalent to Modula2's
	qualified export and marginally more efficient than usual member
	function calls:

		class X {
			// ...
			static f();
		};

		f();	// error (unless there really is a global function f())
		X::f();	// fine

2: initialization of static data members

	In earlier cfronts it was possible to have static data members, but not
	possible to explicitly initialize such static members. The original
	reference manual clearly states this limitation. This limitation means
	that it is impossible to have static members of classes and types that
	require initialization. This have been reported as a serious problem
	many times (about twice a month) and often as a bug by both novice and
	experienced users.

	The solution is simply to remove the restriction and allow initialization
	exactly as is done for member functions. For example:

		struct s {
			static const a;
			static X b;
			// ...
			int f();
		}

		const s::a = 7;
		X s::b(1,2);
		int s::f() { return a; }

About the time of the release of 2.0, I'll post a summary of new features
and short descriptions of each. 

schmidt@pompe.ics.uci.edu (Doug Schmidt) (01/16/89)

In article <8770@alice.UUCP> bs@alice.UUCP (Bjarne Stroustrup) writes:
[ a useful summary of new C++ features leading to increased data abstraction
  and more complete information hiding ]

Now that C++ provides more control over class data and function
members isn't it time to consider adding control over the visibility
of type declaration abstractions as well?  For example, whenever I
present the advantages of C++ to an Ada proponent, the discussion
inevitably turns to C++'s lack of proper nesting for type
declarations.  The Ada advocates claim that this makes it difficult to
control the name space of large programs, especially when combined
with the use of many header files and multiple programmers.

For example, it would be nice to be able to reuse the names of
auxiliary ``helper'' types within the same include file, e.g.:

----------------------------------------
// file search_structure.h

class Binary_Tree {
private:

   class Node { // node for a binary tree
   private:           
      int   Item;
      Node *Left;
      Node *Right;                      
   public:
      Node(int New_Item);
   };

public:
   Binary_Tree();
   Insert(int Item);
   Find(int Item);
};

class Linked_List {
private:

   class Node { // node for a linked list
      int   Item;
      Node *Next;
   };

public:
   Linked_List();
   Insert(int Item);
   Find(int Item);
};
----------------------------------------

   Now, I realize that for small examples like this, the proper choice
of names trivially disambiguates the different types and solves the
problem.  However, for very large programs, it requires a great deal
of effort to keep the type namespace from getting cluttered.  It would
be nice if the language supported this explicitly, rather than relying
upon individuals to keep track of the administrivia.

   I suppose one reason this isn't already implemented is due to the
fact that it might break existing C programs.  However, 

a). This has precedence, in that the proposed changes to the visibility of
    enumerated types declared inside structs and classes in C++ will
    break old C programs.  I'm specifically referring to Lippman and
    Moo's paper from 1988 C++ USENIX Workshop. 

b). It would be ashame not to permit this feature, since it seems
    orthogonal with the data and function abstraction mentioned
    previously.  

Perhaps a reasonable solution would overload the concept of ``static''
even further, allowing lexical (a.k.a. ``static'') structure and class
nestings by prepending the keyword static in front of the keywords,
like this:

----------------------------------------
class Binary_Tree {
private:

   static class Node { // only visible within the proper nesting level
   private:           
   public:
   };

public:
};
//.. same as before
----------------------------------------

Actually, cfront 1.2.1 already accepts this program (perhaps
erroneously!?!).  

Are there other problems with this scheme that I'm overlooking?

thanks, 

Doug Schmidt
--
schmidt@ics.uci.edu (ARPA) |   Per me si va nella citta' dolente.
office: (714) 856-4043     |   Per me si va nell'eterno dolore.
                           |   Per me si va tra la perduta gente.
                           |   Lasciate ogni speranza o voi ch'entrate.

jss@hector.UUCP (Jerry Schwarz) (01/17/89)

In article <4258@paris.ics.uci.edu> Doug Schmidt <schmidt@pompe.ics.uci.edu> writes:
>
>Now that C++ provides more control over class data and function
>members isn't it time to consider adding control over the visibility
>of type declaration abstractions as well?  

It would clearly be desirable and it has been suggested many times
in the past.  

>
>   I suppose one reason this isn't already implemented is due to the
>fact that it might break existing C programs.  However, 
>

Possibly, but this is a minor consideration. The main reason, I
believe, is parsing.  It is non-trivial to write a C++ grammar when
you can count on the lexer to distinguish type identifiers from
ordinary identifiers.  (There are several cases, for example, where
the distinction between an expression and a declaration is tricky) It
is almost impossible if you can't. But its hard to get the lexer to
understand scoping.


Jerry Schwarz
AT&T Bell Labs, Murray Hill

kt@msor.UUCP (Keith Tizzard) (05/23/89)

Can anyone please help to explain a sentence in Stroustrup's reference
manual.

On page 275 section 8.5.1 STATIC MEMBERS the last sentence states:

"No initializer can be specified for a static member, and it cannot
be of a class with a constructor."

Two points arise:

1	the meaning of the piece after the comma is not clear to me -
	does it really mean that if I provide a constructor  for a class
	then I cannot have a static member variable in that class?

	Zortech C++ appears to allow it - is it in error?

2	I understand the first part of the sentence but why does this
	restriction exist?


-- 
Keith Tizzard
MSOR Dept, University of Exeter, Streatham Court, EXETER EX4 4PU, UK
tel: (+44) 0392 264463    email: kt@msor.exeter.ac.uk     kt@msor.UUCP 

ark@alice.UUCP (Andrew Koenig) (05/24/89)

In article <360@msor0.UUCP>, kt@msor.UUCP (Keith Tizzard) writes:

> "No initializer can be specified for a static member, and it cannot
> be of a class with a constructor."

> 1	the meaning of the piece after the comma is not clear to me -
> 	does it really mean that if I provide a constructor  for a class
> 	then I cannot have a static member variable in that class?

No.  It means that if T is a class with a constructor,
no static members of a class may be of type T.  Example:

	class T {
	public:
		T();
	};

	class S {
	public:
		static T t;		// illegal
	};

This will change in C++ 2.0; the example above will be legal
but S::t will have to be defined explicitly somewhere in the
program:

	T S::t;

The reason for requiring a separate definition is that the
declaration of class S may be compiled several times
in separate source files; requiring a single definition
is the only way to cope with the various linkers out there.
-- 
				--Andrew Koenig
				  ark@europa.att.com