Go to the previous, next section.
The GNU compiler provides these extensions to the C++ language (and you
can also use most of the C language extensions in your C++ programs). If you
want to write code that checks whether these features are available, you can
test for the GNU compiler the same way as for C programs: check for a
predefined macro __GNUC__
. You can also use __GNUG__
to
test specifically for GNU C++ (see section `Standard Predefined Macros' in The C Preprocessor).
GNU C++ extends the function-definition syntax to allow you to specify a name for the result of a function outside the body of the definition, in C++ programs:
type functionname (args) return resultname; { ... body ... }
You can use this feature to avoid an extra constructor call when
a function result has a class type. For example, consider a function
m
, declared as `X v = m ();', whose result is of class
X
:
X m () { X b; b.a = 23; return b; }
Although m
appears to have no arguments, in fact it has one implicit
argument: the address of the return value. At invocation, the address
of enough space to hold v
is sent in as the implicit argument.
Then b
is constructed and its a
field is set to the value
23. Finally, a copy constructor (a constructor of the form `X(X&)')
is applied to b
, with the (implicit) return value location as the
target, so that v
is now bound to the return value.
But this is wasteful. The local b
is declared just to hold
something that will be copied right out. While a compiler that
combined an "elision" algorithm with interprocedural data flow
analysis could conceivably eliminate all of this, it is much more
practical to allow you to assist the compiler in generating
efficient code by manipulating the return value explicitly,
thus avoiding the local variable and copy constructor altogether.
Using the extended GNU C++ function-definition syntax, you can avoid the
temporary allocation and copying by naming r
as your return value
as the outset, and assigning to its a
field directly:
X m () return r; { r.a = 23; }
The declaration of r
is a standard, proper declaration, whose effects
are executed before any of the body of m
.
Functions of this type impose no additional restrictions; in particular,
you can execute return
statements, or return implicitly by
reaching the end of the function body ("falling off the edge").
Cases like
X m () return r (23); { return; }
(or even `X m () return r (23); { }') are unambiguous, since
the return value r
has been initialized in either case. The
following code may be hard to read, but also works predictably:
X m () return r; { X b; return b; }
The return value slot denoted by r
is initialized at the outset,
but the statement `return b;' overrides this value. The compiler
deals with this by destroying r
(calling the destructor if there
is one, or doing nothing if there is not), and then reinitializing
r
with b
.
This extension is provided primarily to help people who use overloaded operators, where there is a great need to control not just the arguments, but the return values of functions. For classes where the copy constructor incurs a heavy performance penalty (especially in the common case where there is a quick default constructor), this is a major savings. The disadvantage of this extension is that you do not control when the default constructor for the return value is called: it is always called at the beginning.
It is very convenient to have operators which return the "minimum" or the "maximum" of two arguments. In GNU C++ (but not in GNU C),
a <? b
a >? b
These operations are not primitive in ordinary C++, since you can use a macro to return the minimum of two things in C++, as in the following example.
#define MIN(X,Y) ((X) < (Y) ? : (X) : (Y))
You might then use `int min = MIN (i, j);' to set min to the minimum value of variables i and j.
However, side effects in X
or Y
may cause unintended
behavior. For example, MIN (i++, j++)
will fail, incrementing
the smaller counter twice. A GNU C extension allows you to write safe
macros that avoid this kind of problem (see section Naming an Expression's Type). However, writing MIN
and MAX
as
macros also forces you to use function-call notation notation for a
fundamental arithmetic operation. Using GNU C++ extensions, you can
write `int min = i <? j;' instead.
Since <?
and >?
are built into the compiler, they properly
handle expressions with side-effects; `int min = i++ <? j++;'
works correctly.
goto
and Destructors in GNU C++
In C++ programs, you can safely use the goto
statement. When you
use it to exit a block which contains aggregates requiring destructors,
the destructors will run before the goto
transfers control. (In
ANSI C++, goto
is restricted to targets within the current
block.)
The compiler still forbids using goto
to enter a scope
that requires constructors.
C++ object definitions can be quite complex. In principle, your source code will need two kinds of things for each object that you use across more than one source file. First, you need an interface specification, describing its structure with type declarations and function prototypes. Second, you need the implementation itself. It can be tedious to maintain a separate interface description in a header file, in parallel to the actual implementation. It is also dangerous, since separate interface and implementation definitions may not remain parallel.
With GNU C++, you can use a single header file for both purposes.
Warning: The mechanism to specify this is in transition. For the nonce, you must use one of two#pragma
commands; in a future release of GNU C++, an alternative mechanism will make these#pragma
commands unnecessary.
The header file contains the full definitions, but is marked with
`#pragma interface' in the source code. This allows the compiler
to use the header file only as an interface specification when ordinary
source files incorporate it with #include
. In the single source
file where the full implementation belongs, you can use either a naming
convention or `#pragma implementation' to indicate this alternate
use of the header file.
#pragma interface
#pragma interface "subdir/objects.h"
The second form of this directive is useful for the case where you have multiple headers with the same name in different directories. If you use this form, you must specify the same string to `#pragma implementation'.
#pragma implementation
#pragma implementation "objects.h"
If you use `#pragma implementation' with no argument, it applies to an include file with the same basename(3) as your source file. For example, in `allclass.cc', `#pragma implementation' by itself is equivalent to `#pragma implementation "allclass.h"'.
In versions of GNU C++ prior to 2.6.0 `allclass.h' was treated as an implementation file whenever you would include it from `allclass.cc' even if you never specified `#pragma implementation'. This was deemed to be more trouble than it was worth, however, and disabled.
If you use an explicit `#pragma implementation', it must appear in your source file before you include the affected header files.
Use the string argument if you want a single implementation file to include code from multiple header files. (You must also use `#include' to include the header file; `#pragma implementation' only specifies how to use the file--it doesn't actually include it.)
There is no way to split up the contents of a single header file into multiple implementation files.
`#pragma implementation' and `#pragma interface' also have an effect on function inlining.
If you define a class in a header file marked with `#pragma
interface', the effect on a function defined in that class is similar to
an explicit extern
declaration--the compiler emits no code at
all to define an independent version of the function. Its definition
is used only for inlining with its callers.
Conversely, when you include the same header file in a main source file that declares it as `#pragma implementation', the compiler emits code for the function itself; this defines a version of the function that can be found via pointers (or by callers compiled without inlining). If all calls to the function can be inlined, you can avoid emitting the function by compiling with `-fno-implement-inlines'. If any calls were not inlined, you will get linker errors.
C++ templates are the first language feature to require more intelligence from the environment than one usually finds on a UNIX system. Somehow the compiler and linker have to make sure that each template instance occurs exactly once in the executable if it is needed, and not at all otherwise. There are two basic approaches to this problem, which I will refer to as the Borland model and the Cfront model.
Currently, g++ implements neither automatic model. The g++ team hopes to have a repository working for 2.7.0. In the mean time, you have three options for dealing with template instantiations:
template class A<int>; template ostream& operator << (ostream&, const A<int>&);
This strategy will work with code written for either model. If you are using code written for the Cfront model, the file containing a class template and the file containing its member templates should be implemented in the same translation unit.
A slight variation on this approach is to use the flag -falt-external-templates instead; this flag causes template instances to be emitted in the translation unit that implements the header where they are first instantiated, rather than the one which implements the file where the templates are defined. This header must be the same in all translation units, or things are likely to break.
See section Declarations and Definitions in One Header, for more discussion of these pragmas.
#include "Foo.h" #include "Foo.cc" template class Foo<int>;
for each instance you need, and create a template instantiation library from those. I'm partial to the last, but your mileage may vary. If you are using Cfront-model code, you can probably get away with not using -fno-implicit-templates when compiling files that don't `#include' the member template definitions.
In GNU C++, you can use the keyword signature
to define a
completely abstract class interface as a datatype. You can connect this
abstraction with actual classes using signature pointers. If you want
to use signatures, run the GNU compiler with the
`-fhandle-signatures' command-line option. (With this option, the
compiler reserves a second keyword sigof
as well, for a future
extension.)
Roughly, signatures are type abstractions or interfaces of classes.
Some other languages have similar facilities. C++ signatures are
related to ML's signatures, Haskell's type classes, definition modules
in Modula-2, interface modules in Modula-3, abstract types in Emerald,
type modules in Trellis/Owl, categories in Scratchpad II, and types in
POOL-I. For a more detailed discussion of signatures, see
Signatures: A C++ Extension for Type Abstraction and Subtype
Polymorphism by Gerald Baumgartner and Vincent F. Russo (Tech report
CSD--TR--93--059, Dept. of Computer Sciences, Purdue University,
September 1993, to appear in Software Practice & Experience).
You can get the tech report by anonymous FTP from
ftp.cs.purdue.edu
in `pub/reports/TR93-059.PS.Z'.
Syntactically, a signature declaration is a collection of
member function declarations and nested type declarations.
For example, this signature declaration defines a new abstract type
S
with member functions `int foo ()' and `int bar (int)':
signature S { int foo (); int bar (int); };
Since signature types do not include implementation definitions, you cannot write an instance of a signature directly. Instead, you can define a pointer to any class that contains the required interfaces as a signature pointer. Such a class implements the signature type.
To use a class as an implementation of S
, you must ensure that
the class has public member functions `int foo ()' and `int
bar (int)'. The class can have other member functions as well, public
or not; as long as it offers what's declared in the signature, it is
suitable as an implementation of that signature type.
For example, suppose that C
is a class that meets the
requirements of signature S
(C
conforms to
S
). Then
C obj; S * p = &obj;
defines a signature pointer p
and initializes it to point to an
object of type C
.
The member function call `int i = p->foo ();'
executes `obj.foo ()'.
Abstract virtual classes provide somewhat similar facilities in standard C++. There are two main advantages to using signatures instead:
T
is a subtype of a signature type S
independent of
any inheritance hierarchy as long as all the member functions declared
in S
are also found in T
. So you can define a subtype
hierarchy that is completely independent from any inheritance
(implementation) hierarchy, instead of being forced to use types that
mirror the class inheritance hierarchy.
There is one more detail about signatures. A signature declaration can
contain member function definitions as well as member function
declarations. A signature member function with a full definition is
called a default implementation; classes need not contain that
particular interface in order to conform. For example, a
class C
can conform to the signature
signature T { int f (int); int f0 () { return f (0); }; };
whether or not C
implements the member function `int f0 ()'.
If you define C::f0
, that definition takes precedence;
otherwise, the default implementation S::f0
applies.
Go to the previous, next section.