4.4. Common Misunderstandings with GNU C++

C++ is a complex language and an evolving one, and its standard definition (the ANSI C++ draft standard) is also evolving. As a result, your C++ compiler may occasionally surprise you, even when its behavior is correct. This section discusses some areas that frequently give rise to questions of this sort.

4.4.1. Declare and Define Static Members

When a class has static data members, it is not enough to declare the static member; you must also define it. For example:

class Foo
{
  ...
  void method();
  static int bar;
};

This declaration only establishes that the class Foo has an int named Foo::bar, and a member function named Foo::method. But you still need to define both method and bar elsewhere. According to the draft ANSI standard, you must supply an initializer in one (and only one) source file, such as:

int Foo::bar = 0;

Other C++ compilers may not correctly implement the standard behavior. As a result, when you switch to g++ from one of these compilers, you may discover that a program that appeared to work correctly in fact does not conform to the standard: g++ reports as undefined symbols any static data members that lack definitions.

4.4.2. Temporaries May Vanish Before You Expect

It is dangerous to use pointers or references to portions of a temporary object. The compiler may very well delete the object before you expect it to, leaving a pointer to garbage. The most common place where this problem crops up is in classes like the libg++ String class, that define a conversion function to type char * or const char *. However, any class that returns a pointer to some internal structure is potentially subject to this problem.

For example, a program may use a function strfunc that returns String objects, and another function charfunc that operates on pointers to char:

String strfunc ();
void charfunc (const char *);

In this situation, it may seem natural to write “charfunc (strfunc ());” based on the knowledge that class String has an explicit conversion to char pointers. However, what really happens is akin to “charfunc (strfunc ().convert ());”, where the convert method is a function to do the same data conversion normally performed by a cast. Since the last use of the temporary String object is the call to the conversion function, the compiler may delete that object before actually calling charfunc. The compiler has no way of knowing that deleting the String object will invalidate the pointer. The pointer then points to garbage, so that by the time charfunc is called, it gets an invalid argument.

Code like this may run successfully under some other compilers, especially those that delete temporaries relatively late. However, the GNU C++ behavior is also standard-conforming, so if your program depends on late destruction of temporaries it is not portable.

If you think this is surprising, you should be aware that the ANSI C++ committee continues to debate the lifetime-of-temporaries problem.

For now, at least, the safe way to write such code is to give the temporary a name, which forces it to remain until the end of the scope of the name. For example:

String& tmp = strfunc ();
charfunc (tmp);