
According to Hoyle...
C++0x Part 3: Making Coding Easier
macCompanion
December 2009
by Jonathan Hoyle
jonhoyle@mac.com
http://www.jonhoyle.com
Two months ago,
we began our series
on the changes that will be coming to the
C++ language. Last month,
we examined some of the updates which were expected to take place: fixing embarrassments
and syncing up with
ANSI C99
changes. With this month, we look at some more improvements available to C++
developers of the future. Specifically, changes which make coding easier:
Standard C++ Library Enhancements
The Standard C++ Library, including
STL
(the Standard Template Library), is a generous supply of useful containers and utilities. Despite
its fullness of capabilities, there were still a number of components
missing. C++0x
fills many of these gaps:
regex: A long awaited regular expressions class
array<>: A one-dimensional array containing its size (can be 0)
STL hash classes: unordered_set<>, unordered_map<>, etc.
tuple<>: A templated class, eg:
tuple<int, void *, double>
Mac users are fortunate in that they do not have to
wait for the Standard Library changes: they are available today in
gcc 4 (the
compiler inside
Xcode 2.x
and later). These library additions are within the
library namespace std::tr1:: ("tr1"
stands for
Technical Report #1,
the standard's committee report defining these new classes).
Variadic Templates
For years, the
C language
has allowed functions to have a variable number of parameters. Unfortunately,
this was not true of template arguments within C++98. In C++0x, templates can
have a variable number of types. Here is an example in which a
templated DebugMessage() function
can take advantage of variadic templates:
// Prints to stderr only when DEBUG flag set
template <typename... TypeArgs>
void DebugMessage(TypeArgs... args)
{
#ifdef DEBUG
... // Implement writing to stderr
#else
// Do nothing
#endif
}
// Later in code
DebugMessage("The value of n = ", n);
DebugMessage("x = ", x, ", y = ", y, ", z = ", z);
DebugMessage("TRACE: ",
"time = ", clock(),
", filename = ", __FILE__,
", line number = ", __LINE__,
", inside function = ", __func__);
This new flexibility with macros will make it easier
for developers wishing to use them, as their current restrictions make them occasionally
cumbersome.
Visit
here
for more on Variadic Templates.
Delegating Constructors
Other languages, such as
C#, allow one
class constructor
to invoke another. In C++98, this was not possible, thus requiring the class
designer to create a separate initialization function, if it wished to use common
code across multiple constructors. In C++0x, this becomes available, as the
following example shows:
class X
{
public:
X(); // default constructor
X(void *ptr); // takes a pointer
X(int value); // takes an int
};
X::X(): X(NULL) // calls X(void *)
{
... // other code
}
X::X(void *ptr): X(0) // calls X(int)
{
... // other code
}
X::X(int value) // does not delegate
{
... // other code
}
With constructor delegation, C++ class designers can
simplify their implementations.
Visit
here
for more on Delegating Constructors.
NULL Pointers
In ANSI C,
NULL is
defined as (void *) 0. In
C++, the use of NULL is
deprecated. Why? Because unlike in C, it is illegal in C++ to directly
assign a void pointer
to any other type of pointer (that is, not without a
cast):
void *vPtr = NULL; // legal C, legal C++
int *iPtr = NULL; // legal C, illegal C++
// Cannot assign void * to int * in C++
int *iPtr = 0; // legal C++
However, the proliferation of NULL in
C++ code remains so great, many compilers simply generate a warning, not an error,
when such a pointer assignment mismatch takes place. Others
redefine NULL in
C++ as simply 0. Despite
these occasional compiler courtesies, this is still very confusing for beginning C++
programmers, especially in examples such as these:
void foo(int); // takes an int
void foo(char *); // takes a char ptr
foo(0); // Is this "0" to be a ptr or a number?
foo(NULL); // No matching prototype (no void *)
For this reason, C++0x
introduces nullptr ,
a type-safe nil pointer which can be used with any pointer type, but is not compatible
with any integral type:
char *cPtr1 = nullptr; // a null C++ pointer
char *cPtr2 = 0; // legal, but deprecated
int n = nullptr; // illegal
X *xPtr = nullptr; // can be used with any ptr type
void foo(int); // takes an int
void foo(char *); // takes a char ptr
foo(0); // calls foo(int)
foo(NULL); // calls foo(char *)
Essentially, C++ programmers now have a type-safe
replacement for NULL ,
which matches similar behavior in other more recent languages.
Visit
here
for more on nullptr.
The Amazing Return of auto
When the C language first evolved, the auto keyword
was used to specify to the compiler that a variable was being allocated on the stack,'
for example:
auto x; /* implicitly an int, placed on the stack */
Then ANSI C was ratified in 1989, and the implicit int rule
was removed:
auto x; /* now illegal in ANSI C */
int x; /* OK, auto assumed */
auto int x; /* OK, but redundant */
Since that time, auto remained
a keyword in the C (and later C++) languages, even though virtually no one had used
it since the 1970's, due to its being completely redudndant. Now after over
30 years of disuse, the C++0x standard reintroduces
the auto keyword
to mean that the variable's type is implied by its initializer, as so:
auto x = 10; // x is an int
auto y = 10.0; // y is a double
auto z = 10LL; // z is a long long
const auto *p = &y; // p is a const double *
The savings becomes even more significant with complicated
types, such as in the following example:
void *foo(const int doubleArray[64][16]);
auto myFcnPtr = foo; // myFcnPtr is of type: "void *(const int(*)[16])"
In addition, auto becomes
useful for temporary variables whose types aren't important but merely just have to
match. Consider the following function which walks through an STL container:
void foo(const vector<MySpace::MyClass> &x)
{
for (auto ptr = x.begin(); ptr != x.end(); ptr++)
{
... // code accessing the data
}
}
Without auto,
the type for variable ptr that
would have to be put in this for-loop
would be:
const vector<MySpace::MyClass>::const_iterator
Moreover, any change to this container, such as changing
it from a vector<> to
a list<>,
or changing the class name or namespace, would require changes in
the ptr variable
definition, despite the fact its type is completely unnecessary to note (other than
for the compiler).
Note that an initializer is still required to
use auto in
C++0x:
auto x; // still illegal in C++0x
But suppose you knew what type you wanted (based upon
another variable) but did not want to initialize it just yet? The
new decltype keyword
is available for just such purposes, as the following example shows:
bool SelectionSort(double data[256], double tolerance);
bool BubbleSort(double data[256], double tolerance);
bool QuikSort(double data[256], double tolerance);
decltype(SelectionSort) mySortingFunction;
if (bUseSelectionSort)
mySortingFunction = SelectionSort;
else if (bUseBubbleSort)
mySortingFunction = BubbleSort;
else
mySortingFunction = QuikSort;
// Now sort my data
mySortingFunction(dataToSort);
With the inclusion
of auto & decltype in
C++0x, a great deal of simplicity is restored to C++ development, without any loss
of power.
Visit
here
for more on the auto keyword.
Coming Up:
C++0x Part 4: Smart Pointers
C++0x Part 5: Rvalue References
C++0x Part 6: Final Thoughts
http://www.maccompanion.com/macc/archives/December2009/Columns/AccordingtoHoyle.htm