http://www.developer.com/net/cplus/article.php/3702596/Peeking-Further-into-C00X.htm
In last month's installment, you covered the context of C++ standards development, TR1, and the rationale for expanding the scope of C++. The next C++ standard, code-named "C++00X", is heading down the home stretch towards ratification. Currently, it is expected to be named C++09 as of the latest report (N2336) released in July 2007. In this article, you peek further into what C++00X means to the average C++ programmer in the trenches. Because there are dozens of WG21 proposals on the table as of this writing, I will focus this article solely on proposals that were greenlighted at the July 2007 meeting, rather than discussing features that have been integrated in past months' or years' work. Recently integrated into the working paper, Generalized Constant Expression represents the fourth iteration of attempts to normalize the working of constants in the C++ language. Although constants are everywhere in C++ programs (including const and enum), their participation in expressions has a certain degree of ambiguity under current rules. N2335 addresses all of these concerns in four specific categories: One problem this fixes is the bitmask type when it's implemented by an enum with overloaded operators. This is just one of three allowable implementations by the compiler; when it's used, the bitmask is no longer constant in the sense that it can be evaluated at compile time. The new constexpr keyword addresses this and many other things that allow for compile-time optimization of things that formerly were deferred until execution time. It is possible to be surprised by expressions that (to someone) "look const" but are not. For example, consider Here, S::size is indeed initialized with a constant expression, but that initialization comes "too late" to make S::size a constant expression; consequently, limit may be dynamically initialized. Again, constexpr will come to the rescue. The new distinction Constant-Expression Function marries the best attributes of #define macro functions with the type safety, predictability of const, and guaranteed compile-time evaluation. In particular, a function is a constant-expression function if and only if it satisfies these criteria: and every single thing that combines to produce expr is also constant. Interestingly, a constant-expression function may be called with non-constant expressions (for example, variables). In that case, there is no requirement that the resulting value be evaluated at compile-time. Here are some examples: The 1999 revision of the C standard introduced the concept of 'a predeclared identifier'. N1970 carries that concept forward into C++ in the name of upward compatability, an attribute that the standards committee holds dear. In the past, the role of these diagnostic aids was a part of the macro preprocessor, a mechanism that the standards committee tries to relegate as much as possible. Basically, these predeclared identifiers allow you to construct intelligent error messages that point out the line number, file name, and function name where a problem is taking place. __func__ is the equivalent of a local declaration like: Traditionally, the double_underscore ("__") denotes a compiler vendor-specific identifier, although it is now a universal identifier in this particular case. Here's a typical usage: WG21 proposal N2220 provides further clarification of how __func__ changes over the course of the runtime environment: One thing missing in the standards as of late is a clear definition of what constitutes a POD object. POD stands for Plain Old Data and begs the question "What kinds of things can you do with classes that resemble old C structs for all intents and purposes?" It is not a silly question because compiler optimization, STL implementation efficiency, and thread-safe operation rely heavily on knowing when something can be bitwise-copied, like a dumb struct and when the consequences of ctors and dtors comes into requirement. It has evolved over time to N2324, which provides keywords for expressing atomic operations, a key requirement for thread-safe code. A discussion of POD semantics lies outside of what I can cover in this article. The decltype feature lets you get the type of an expression, so that you can do things with the type (for example, declare more variables of that type) without knowing in advance what the type is or how to spell it. The decltype() is a great boon to generic programming with templates, and without the runtime cost of reflection in other languages (think of TypeOf() in .NET parlance). For example, say that you're handed an iterator, and you want to know what type it refers to. Today, you need to ask the iterator for its value_type, which is a manual "traits" convention everyone is expected to follow when writing and using iterators: With decltype, you could instead write: This is all in service of making C++ code easier to write and with exposing fewer awkward implementation details. On a similar note, you may notice the explosion of typedefs needed to make using certain STL types legible. This is addressed by the new auto keyword that basically says "Make me a variable of the correct return type" and is bound to save hours of searching manuals and copy/paste madness. Instead of writing the programmer can use the shorter Which form would you try to teach new programmers trying to learn C++? Yes, I think so too! Victor Volkman has been writing for C/C++ Users Journal and other programming journals since the late 1980s. He is a graduate of Michigan Tech and a faculty advisor board member for the Washtenaw Community College CIS department. Volkman is the editor of numerous books, including C/C++ Treasure Chest and is the owner of Loving Healing Press. He can help you in your quest for open source tools and libraries; just drop an e-mail to sysop@HAL9K.com.
Peeking Further into C++00X
October 1, 2007
Expanding the Scope of C++!
Generalized Constant Expressions
Fixing the Bitmask
When const Isn't
struct S {
static const int size;
};
const int limit = 2 * S::size; // dynamic initialization
const int S::size = 256;
// dynamic initialization
const int z = numeric_limits<int>::max();
Constant-Expression Functions
return expr;
constexpr int square(int x)
{ return x * x; } // legal
constexpr int abs(int x)
{ return x < 0 ? -x : x; } // legal
constexpr int next(int x)
{ return ++x; } // error: increment cannot be evaluated at
// compile time
constexpr int g(int n) // error: body not just ''return expr''
{
int r = n;
while (--n > 1) r *= n;
return r;
}
constexpr int fac(int x)
{ return x > 2 ? x * fac(x - 1) : 1; } // error: fac() not
// defined before use
C99 Compatibility : __func__ and Predeclared Identifiers
static const char __func__[] = "function-name";
include <cstdio>
namespace example
{
void myfunc()
{
std::printf("Error in function %s\n", __func__);
/* ... */
}
}
namespace N { void f(); }
void N::f() { } // __func__ is "f"
struct S {
S() : s(__func__) { } // okay, s points to "S"
~S() { } // __func__ is "~S"
// __func__ is "conversion operator"
operator int() { }
template<class T> int g();
const char *s;
};
S operator +(S,S) { } // __func__ is "operator+"
template<> int S::g<int>() { } // __func__ is "g"
Tracking the PODs
Even More Runtime Type Information
template<typename Iter>
void f( Iter it ) {
Iter::value_type v = *it;
...
}
template<typename Iter>
void f( Iter it ) {
decltype(*it) v = *it;
...
}
for (vector<int>::const_iterator itr = myvec.begin();
itr != myvec.end(); ++itr)
for (auto itr = myvec.begin(); itr != myvec.end(); ++itr)
About the Author