5147 c++0 special report op
TRANSCRIPT
-
8/8/2019 5147 c++0 Special Report Op
1/42
C++Ox:The Dawning of a New Standard
Special Report Bundle
-
8/8/2019 5147 c++0 Special Report Op
2/42
Contents
C++0x: The Dawning of a New StandardIt's been 10 years since the first ISO C++ standard, and 2009 will bring us the second. In this special
report, DevX delves into the new features being discussed by the standards team. Learn how these
new features will revolutionize the way you code.
Overview: C++ Gets an OverhaulIt's been 10 years since the first ISO C++ standard, and 2009 will bring us the second.Learn about the new features being added and how they will revolutionize the language.
Easier C++: An Introduction to ConceptsC++0x concepts bring the full power of the Generic Programming paradigm to C++,
making templates more expressive, easier to write, and easier to use. Spectacularly poor
template error messages are a thing of the past!
Simpler Multithreading in C++0x
The new standard will support multithreading, with a new thread library. Find out how thiswill improve porting code, and reduce the number of APIs and syntaxes you use.
The State of the Language: An Interview with Bjarne StroustrupC++ founding father assesses the language on the eve of its new standard.
Timeline: C++ in RetrospectFrom its nascent pre-processor in 1979 to today's incredibly sophisticated language
features and libraries, we've documented each step along the way.
-
8/8/2019 5147 c++0 Special Report Op
3/42
Overview: C++ Gets an Overhaul
C++0x:The Dawning of a New Standard
-
8/8/2019 5147 c++0 Special Report Op
4/42
Overview: C++ Gets an Overhaul
Originally from http://www.devx.com/SpecialReports/Article/38884
Overview: C++ Gets an OverhaulIn this overview of the changes proposed for the new standard, you'll get an idea of
the kinds of improvements you can expect this time around.by Danny Kalev
en years after the ratification of the first ISO C++ standard, C++ is heading for no less than arevolution. C++0x, the new C++ standard due in 2009, brings a new spirit and new flesh into the
software development world. Brace yourself for state-of-the-art design idioms, even better
performance, and a plethora of new features such as multithreading, concepts, hash table, rvalue
references, smarter smart pointers, and new algorithms. No doubt you'll find a lot to like in C++0x!
New Core Features
The two most important features of C++0x are concepts and concurrency support. Conceptsenable programmers to specify constraints on template parameters, thus making generic
programming and design immensely simpler and more reliable (see Douglas Gregor's excellent
introduction to concepts). Variadic templates, template aliases (also called template typedefs), and
static_assertthough not directly related to conceptswill also make the use of templates in genericlibraries more intuitive, flexible, and less error prone.
The importance of a standardized concurrency APIin C++ can't be overstated: As multicore
processors are becoming widespread, you simply can't afford to remain stuck in the single-
threaded era, or compromise on platform-dependent APIs. At last, there's a portable, standardized
and efficient multithreading library for C++. To get a glimpse of the new concurrency facilities of C++0x, you're welcome to read Anthony Williams' brilliant introduction to multithreading in C++0x.
You can find additional info about thread-local storage here.
Rvalue references are yet another silent revolution. While most users will probably not even know
they exist (read my interview with Bjarne Stroustrup), rvalue references enable library designers to
optimize containers and algorithms by implementing move semantics and perfect forwarding
easily, thus reducing unneeded copy operations.
C++Ox:The Dawning of a New Standard
http://www.devx.com/SpecialReports/Article/38884http://www.devx.com/SpecialReports/Article/38864http://www.devx.com/SpecialReports/Article/38864http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2191.pdfhttp://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1406.pdfhttp://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=343http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=343http://www.devx.com/SpecialReports/Article/38883http://www.devx.com/cplus/10%20Minute%20Solution/37436/0http://www.devx.com/SpecialReports/Article/38813http://www.devx.com/cplus/10%20Minute%20Solution/34577/0http://www.devx.com/cplus/10%20Minute%20Solution/34577/0http://www.devx.com/SpecialReports/Article/38813http://www.devx.com/cplus/10%20Minute%20Solution/37436/0http://www.devx.com/SpecialReports/Article/38883http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=343http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1406.pdfhttp://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2191.pdfhttp://www.devx.com/SpecialReports/Article/38864http://www.devx.com/SpecialReports/Article/38864http://www.devx.com/SpecialReports/Article/38884 -
8/8/2019 5147 c++0 Special Report Op
5/42
Overview: C++ Gets an Overhaul
Automatic type deduction is made possible by the new keywords auto and decltype which deduce
the type of an object from its initializer and capture the type of an expression without having to
spell it out, respectively. Adding auto and decltype also paves the way for a new function
declaration syntax. The function's return type appears after the -> sign:
auto func(int x)->double {return pow(x);}
Lambda expressions and closures are another prominent feature of C++0x. A lambda expression
is a nameless function defined at the place where it's called. It is similar to a function object except
that the programmer is rid of the burden of declaring a class with a constructor, defining an
overloaded () operator and an instantiating a temporary object of that classthis tedium nowbecomes the compiler's job. Here's an example of a lambda expression:
//a lambda expression is used as an argument
myfunc([](int x, int y) -> int {return x+y;} )
The lambda expression is indicated by the lambda introducer [] followed by a parameter list in
parentheses. The optional return type comes next, following the -> sign. Finally, the lambda block
itself is enclosed in braces.
Convenience Features
Some C++0x features are meant to simplify recurring programming tasks and minimize boilerplate
code. Most of these "convenience features" were borrowed from other programming languages.
These convenience features include:
A null pointer literal called nullptr
Strongly-typed enumerations
Delegating and inheriting constructors
Listing 1 demonstrates these features.
C++0x also removes some embarrassments from the language. For example, C++03 has at least
three different forms of initialization:
int x=0;
int x(0);
int y[2]={1,2};
C++0x defines a unified initialization notation which can even be used in the declaration of
dynamically allocated arrays:
http://www.devx.com/cplus/10%20Minute%20Solution/37671/0http://www.devx.com/cplus/10%20Minute%20Solution/37854http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=254http://www.devx.com/cplus/10%20Minute%20Solution/35167/0http://www.devx.com/cplus/10%20Minute%20Solution/33504/0http://www.devx.com/cplus/10%20Minute%20Solution/33052/0http://www.devx.com/SpecialReports/Article/38884/1763?supportItem=1http://www.devx.com/SpecialReports/Article/38884/1763?supportItem=1http://www.devx.com/cplus/10%20Minute%20Solution/33052/0http://www.devx.com/cplus/10%20Minute%20Solution/33504/0http://www.devx.com/cplus/10%20Minute%20Solution/35167/0http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=254http://www.devx.com/cplus/10%20Minute%20Solution/37854http://www.devx.com/cplus/10%20Minute%20Solution/37671/0 -
8/8/2019 5147 c++0 Special Report Op
6/42
Overview: C++ Gets an Overhaul
int* a = new int[4] {1, 10, 20,95 };
vector vs={"ab","cd","ef"};
class S {
int arr[3];
public:
S() : arr{ 1, 2, 3 } {}};
More Control in Your Hands
Certain new core features are meant to provide a standardized and uniform mechanism for
controlling the compilation and execution environment programmatically. For example, take
memory alignment. In C++03, you have to resort to compiler-dependent, non-portable hacks if you
want to override the default alignment of your data. C++0x introduces two new operators: alignof
and alignas that query and override the alignment requirements of your data, respectively:
alignas (16) class Session
{
long long timestamp;
int authorizations;
bool active;
//...
};
cout
-
8/8/2019 5147 c++0 Special Report Op
7/42
-
8/8/2019 5147 c++0 Special Report Op
8/42
Overview: C++ Gets an Overhaul
Perhaps the most popular Standard Library extension is the set of new smart pointer classes: std::
tr1::shared_ptrand its little known sibling, std::tr1::weak_ptr.
The class template std::tr1::array wraps a built-in array with a standard container's interface.
Similarly, a tuple groups an arbitrary number of objects of unrelated types in single, container-like
object. With respect to algorithms, 11 new algorithms were voted into draft standard in June 2008.Some of these algorithms fill holes in the C++98 standard, whereas others simplify common tasks.
Here are some of them:
all_of( first, last, pred): This returns true if all elements in the range satisfy the predicate
pred
any_of( first, last, pred): This returns true if any element in the range satisfies pred
copy_n(first, n, result): This copies n elements into the result
iota( first, last, value): For each element in the range, this assigns value and pre-increment
value by ++value
none_of( first, last, pred): This returns true if none of the elements in the range satisfiespred
Finally, the range and subrange class templates of C++0x bundle pairs of iterators, thus
significantly simplifying certain sequence operations and compacting the signatures of many
popular algorithms.
The Road Ahead
The vast number of new features forces the committee to work at an incredible speed. A clear
statement of intent was made to complete work on the new standard at the San Francisco meeting
of September 2008 in order to achieve publication in 2009. To meet this ambitious timetable theplan is to vote out a feature complete Working Draft at the next meeting. A Final Committee Draft
will be issued from the following meeting, allowing at least a minimal time for review and
"integration testing" of the new featuresparticularly a conceptualized Standard Library.Danny Kalev is a certified system analyst and software engineer specializing in C++ and
theoretical linguistics. He has an MA degree in general linguistics and is the author ofInformit C++
Reference Guide and The ANSI/ISO Professional C++ Programmer's Handbook. Danny was a
member of the C++ standards committee between 1997 and 2000. Danny recently finished his MA
in general linguistics summa cum laude. In his spare time he likes to listen to classical music, read
Victorian literature, and explore new natural and formal languages alike. He also gives lecturesabout programming and applied linguistics at academic institutes.
DevX is a division of Jupitermedia Corporation
Copyright 2007 Jupitermedia Corporation. All Rights Reserved. Legal Notices
http://www.devx.com/cplus/10MinuteSolution/28347http://www.devx.com/cplus/10MinuteSolution/28347http://www.devx.com/cplus/10MinuteSolution/27219/0/page/2http://www.devx.com/cplus/10MinuteSolution/21712http://www.devx.com/cplus/10%20Minute%20Solution/36394/0http://www.informit.com/title/0536941467http://www.informit.com/title/0536941467http://www.amazon.com/exec/obidos/tg/detail/-/0789720221http://www.internet.com/corporate/legal.htmlhttp://www.internet.com/corporate/legal.htmlhttp://www.amazon.com/exec/obidos/tg/detail/-/0789720221http://www.informit.com/title/0536941467http://www.informit.com/title/0536941467http://www.devx.com/cplus/10%20Minute%20Solution/36394/0http://www.devx.com/cplus/10MinuteSolution/21712http://www.devx.com/cplus/10MinuteSolution/27219/0/page/2http://www.devx.com/cplus/10MinuteSolution/28347http://www.devx.com/cplus/10MinuteSolution/28347 -
8/8/2019 5147 c++0 Special Report Op
9/42
Easier C++: An Introduction to Concepts
C++0x:The Dawning of a New Standard
-
8/8/2019 5147 c++0 Special Report Op
10/42
Easier C++: An Introduction to Concepts
Originally from http://www.devx.com/SpecialReports/Article/38864
Easier C++: An Introduction to ConceptsC++0x concepts bring the full power of the Generic Programming paradigm to C++,
making templates more expressive, easier to write, and easier to use. Spectacularlypoor template error messages are a thing of the past!
by Douglas Gregor
very C++ programmer has had one of those days: A simple use of a template library turns intoa nightmare, with pages upon pages of error messages streaming out of the compiler. Somewhere
in that proverbial haystack are the clues you will need to determine exactly what went wronganerror about a missing + operator here, an incompatible type assignment therebut you know youare in for a long search through the grisly internals of the template library, or a visit from your local
C++ template guru.
This article is not about C++ template error messages, but they are indicative of a far more
general problem with the C++ template system. Most errors in the use of templates come from a
misunderstanding between the author of the template and the user of the template. The author of
the template expects the user's type to provide some specific set of operations, say, a + operator
for addition and a copy assignment operator, which I'll refer to as the template requirements.
When the user provides a type with the appropriate operations, i.e., the type satisfies the template
requirements, everything works. However, when the user's type is missing some operations, the
compiler reports the error as soon as the template tries to use that operation, which is often deep
in the implementation of the template library. Thus, the template requirements are effectively a
contract between the template author and user, and debugging a template error message is the
act of trying to determine who broke the contractand how.Enter: Concepts
Concepts is a language feature planned for C++0x that allows programmers to express the
template requirements as part of the templates source code. Placing requirements on a template
formalizes the contract between template author and user, allowing the compiler to ensure that
both parties uphold their end of the bargain. The template author is permitted to use only
operations that he has required the user to provide, while the template user is allowed to supply
only types that provide all of the operations that the template requires. When both parties uphold
C++Ox:The Dawning of a New Standard
http://www.devx.com/SpecialReports/Article/38864http://www.devx.com/SpecialReports/Article/38864 -
8/8/2019 5147 c++0 Special Report Op
11/42
-
8/8/2019 5147 c++0 Special Report Op
12/42
Easier C++: An Introduction to Concepts
user and author so that the compiler can verify that both parties abide by the terms of the contract.
Next, you'll see how the compiler verifies both sides of the contract.
User's Side of the Contract
To call the min() function, the user must provide a type for T that meets the LessThanComparable
concept's requirements. Because C++ integers provide a less-than operator, the call min(17, 42) is
a reasonable test. However, this call will fail with an error at the call site (not in the implementationof min()) stating that int is not LessThanComparable. The problem, in this case, is that the
compiler does not know what the less-than operator in LessThanComparable actually means, and
therefore cannot determine whether the built-in less-than provided for integers agrees with that
meaning. Therefore, the user states explicitly that int meets the LessThanComparable concept's
requirements through the use of a concept map:
concept_map LessThanComparable { }
Concept maps state that a particular type meets the concept's requirements. When one attempts
to use a constrained template like min(17, 42), the compiler searches for a concept mapLessThanComparable and will find the concept map defined above, so the call succeeds.
Concept maps play a second role in checking the user's side of the contract, because the concept
maps themselves are verified against the body of the concept. In the concept map above, the
compiler verifies that the int type has a suitable less-than operator (it happens to be a built-in
operator), which returns a type that is convertible to bool. This checking can be seen when one
attempts to write an incorrect concept map, for instance:
concept_map LessThanComparable {
} // error: no operator< for std::complex
Because std::complex does not provide a less-than operator, the compiler will produce an
error message stating that this type does not meet the LessThanComparable concept's
requirements. Because you can't even define the concept map to state that complex numbers are
LessThanComparable, you can't attempt to use the min() function. Most importantly, you never
have to look into the actual body of min() to determine that there was an error; the requirements
have all of the information you need.
For trivial concepts such as LessThanComparable, the need to write a concept map for every type
can become a bit onerous. For this reason, there is a special kind of concept, called an autoconcept, for which the compiler will automatically provide concept maps whenever they are
needed. If you change the definition of the LessThanComparable concept to the following, users
will no longer have to write concept maps for LessThanComparable to call the min() function:
auto concept LessThanComparable {
bool operator
-
8/8/2019 5147 c++0 Special Report Op
13/42
Easier C++: An Introduction to Concepts
Author's Side of the Contract
When the author of a template places requirements on that template, the author is also bound by
the terms of the contract. In particular, the author cannot use an object of type T with any
operations not specified in the contract. With the min() function, for example, the body of the
function can use only the < operator on objects of type T, because only the < operator is specified
by the LessThanComparable concept. Suppose you tried to implement a max() function with
LessThanComparable as follows:
template
requires LessThanComparable
const T& max(const T& x, const T& y) {return x > y? x : y; // error: no operator>(T, T)
}
In this example, the author specified in the contract that the type T would provide a ''. The compiler will produce an error message when it processes the
template stating that no such '>' operator exists. Therefore, the template author is bound by the
same contract as the user, and there cannot be any surprises where the template author states
one requirement in the contract but uses a slightly different operation in the implementation. This
is exactly the kind of problem that occurs with template libraries today: Because most types that
max() would be tested with will have both less-than and greater-than operations, the latent
errorthat the function requires less-than but uses greater-thanis unlikely to be detected by testing.With concepts, however, the compiler detects this form of error immediately.
Because both the template author and user are held to the same contract, the compiler canprovide an extremely important guarantee: If both the template's implementation and the use of
the template meet the contract's requirements, the instantiation of that template will not fail. There
are two practical effects to this guarantee. First, concepts eliminate the spectacularly poor error
messages produced by long instantiation backtraces, because the failures occur before template
instantiation is even attempted. Second, both users and library authors can have a far greater
level of confidence in template libraries using concepts than in the corresponding pre-concept
libraries, because the compiler takes a more active role in verifying the correctness of templates.
The Perimeter of a Polygon
Listing 2 contains a simple algorithm that computes the perimeter of the polygon it is given, by
summing the lengths of each of the sides of the polygon. Polygons are described by the Polygon
concept, which provides functions that determine the number of sides and the length of a given
side.
The Polygon concept's requirements can be satisfied by many different data types. For example,
you can implement a simple triangle class that can be passed to the perimeter() function:
class triangle {
http://www.devx.com/SpecialReports/Article/38864/1763?supportItem=2http://www.devx.com/SpecialReports/Article/38864/1763?supportItem=2 -
8/8/2019 5147 c++0 Special Report Op
14/42
Easier C++: An Introduction to Concepts
public:int sides[3];
};
int num_sides(const triangle& tri) { return 3; }int side_length(const triangle& tri, int index) {
return tri.sides[index];}
concept_map Polygon { }
The triangle class provides the operations required by the Polygon concept, num_sides() and
side_length(). Note, however, that while the Polygon concept expects side_length() to return a
double, triangle's version of side_length() returns an int. This is not an error. Concept maps
provide one level of conversions, which will automatically convert the int returned by triangle's
num_sides() into the double expected by users of the Polygon concept. This way, minor
mismatches in the interface between the template user and the template author are resolvedautomatically in the concept map, making constrained templates more widely applicable.
Associated Types
The implementation of perimeter() works with the triangle class, but it is suboptimal; despite the
fact that the sides of the triangle are specified in terms of integers, all of the computation in
perimeter() is performed via floating-point arithmetic. To solve this problem, the algorithm should
instead perform the computation using the same type that the polygon uses to express the lengths
of its sides, which may vary from one type to another.
Concepts provide a way to express such types, which vary from one use of a concept to another:
associated types. Associated types are declared within the body of a concept via the typename
keyword (using the same syntax as template type parameters), and can be used to describe
operations within the concept. You can now revise the Polygon concept to introduce an associated
type named length_type, which is used to express the length of a single side in the polygon, and
therefore is the return type of the side_length() operation:
concept Polygon {
typename length_typeint num_sides(const P&);length_type side_length(const P&, int index);
}
Because the types of concept operations are important wherever the concepts are used, you can
refer to an associated type as a nested type within the concept. For example, an updated
perimeter() algorithm should refer to the length_type of the Polygon concept explicitly, rather than
using double:
-
8/8/2019 5147 c++0 Special Report Op
15/42
Easier C++: An Introduction to Concepts
templaterequires Polygon
Polygon
::length_type perimeter(const P& poly) {
Polygon
::length_type sum = 0; // error!
for (int i = 0; i < num_sides(poly); ++i)
sum += side_length(poly, i); // error!return sum; // error!
}
Here, the uses of double are replaced with Polygon
::length_type, which has made the
algorithm capable of handling different length types, and therefore more reusable. However, this
change has introduced some new errors that will be detected by the compiler. Previously, the
algorithm was relying on the built-in operations provided by double: construction from an integer,
addition via +=, and copy-construction of the return value. With an arbitrary length_type, you can
no longer assume that all of these operations are available. They have to be specified as
requirements on the length type, which the user's length type must satisfy.
Member-Function Requirements
To express the length type's requirements, we introduce another new concept, Numeric. The
Numeric concept provides the essential operations required to sum a value:
auto concept Numeric {
T::T(const T&); // copy constructionT::T(int); // construction from an int
T::~T(); // destructor
T& operator+=(T&, const T&); // addition}
The first three operations in the Numeric concept require a copy constructor (which can be
satisfied by a built-in initialization), the ability to construct a value of type T from an int, and the
ability to destroy an object of type T. Each of these operations is stated using member function
syntax with the T:: prefix, specifying that these operations apply to objects of type T. The same
syntax can be used to describe specific member functions requirements (for example, T::clone()).
Armed with the Numeric concept, you can express the perimeter() algorithm's full requirements as:
template
requires Polygon
&& NumericPolygon
::length_type perimeter(const P& poly);
The "&&" in the requires clause states that both requirements must be satisfied for the algorithm to
be usable, that is, the type P must be a Polygon and its length_type must be Numeric. Any
number of different requirements can be added to a template using "&&", including multiple
-
8/8/2019 5147 c++0 Special Report Op
16/42
Easier C++: An Introduction to Concepts
requirements on the same types.
Associated Requirements
Adding the Numeric requirement to the perimeter() concept is correct, but it is not ideal. The
requirement that the length_type of a Polygon be Numeric isn't solely a property of perimeter(): It'sa more universal property that polygons only make sense if their length types are proper numeric
types. To support this common usage, concepts support associated requirements, which specify
extra requirements on the associated types of a concept within the concept body. Here is the final
version of the Polygon concept, which makes use of associated requirements:
concept Polygon {typename length_type;
requires Numeric;
int num_sides(const P&);
length_type side_length(const P&, int index);}
With this change, you can remove the explicit Numeric requirement from the perimeter() algorithm,
because the length type of a Polygon is always numeric. For the final declaration of this perimeter
() algorithm, I've also employed two syntactic shortcuts, which simplify the expression of template
requirements and simplify access to associated types:
template
P::length_type perimeter(const P& poly);
The first shortcut is the use of Polygon P, which states that P is a template type parameter whose
requirement is Polygon
; it is identical to writing the same requirement within a requires clause.
The second shortcut is the use of P::length_type, which searches for an associated type named
length_type within the requirements placed on P, and is identical to Polygon
::length_type.
Syntax Remapping with Concept Maps
All the concept maps so far have had empty bodies within their curly braces, and have been used
to state (and check) that a type meets the concept requirements. However, concept maps need
not be empty: they can contain definitions that specify how a type meets the conceptrequirements, and can even contain new functions that allow one to map the syntax provided by a
type to the syntax expected by the concept. For example, start with a simple rectangle structure:
struct rectangle {
double left, top, right, bottom;};
-
8/8/2019 5147 c++0 Special Report Op
17/42
Easier C++: An Introduction to Concepts
You could add appropriate num_sides() and side_length() functions to this type to make it look like
a Polygon. But, this expands the interface in a way that might not be desirable if the only purpose
is to use the perimeter() function. Instead, you can write a concept map that makes the rectangle
look like a polygon:
concept_map Polygon {
typedef double length_type;
int num_sides(const rectangle&) { return 4; }
double side_length(const rectangle& rect, int index) {return index % 2? fabs(rect.right rect.left)
: fabs(rect.bottom rect.top);
}
}
In this concept map, the Polygon concept's requirements are satisfied by the body of the concept
map itself. The functions provided by the concept map are visible only through the concept itself,
and are not part of the public interface of the rectangle structure (which has not changed). Only
those constrained templates that operate on Polygons will see this particular view of the rectangle
structure.
Through this syntax-remapping mechanism, an existing type can be adapted to meet an existing
concept's requirements without changing either the type or the concept, allowing far greater reuse
of generic algorithms than is possible without concept maps.
Concept Refinement and Concept-Based Overloading
It is often the case when implementing a generic algorithm that there are several possible
implementations, each of which involves different trade-offs. Some implementations may apply to
a wide variety of data types (are very generic) while others operate more efficiently on a more
narrow range of data types (are more specialized). For example, the generic perimeter() algorithm
operates on any polygon in linear time, but there is a more efficient implementation: If you know
that the polygon is an equilateral polygon (where all sides have the same length), you could
compute the perimeter with a single multiplication. Like Polygon, EquilateralPolygon is a concept
that describes types that behave like polygons.
You could implement an EquilateralPolygon separately from the Polygon concept, but doing so
misses an important opportunity for reuse, because every equilateral polygon is also a polygon.
You can therefore express EquilateralPolygon as a refinementof the Polygon concept:
concept EquilateralPolygon : Polygon
{ }
-
8/8/2019 5147 c++0 Special Report Op
18/42
Easier C++: An Introduction to Concepts
As the syntax implies, concept refinement is similar to concept "inheritance," because the refining
concept (EquilateralPolygon) inherits all of the base concept's requirements (Polygon).
Additionally, a type that is an EquilateralPolygon can be used in any algorithm that expects a
Polygon, meaning that this square class could be used with the existing perimeter() algorithm:
class square {
public:int length;
};
concept_map EquilateralPolygon {typedef int length_type;
int num_sides(const square&) { return 4; }
int side_length(const square& sq, int) {
return sq.length;}
}
Concept refinement describes a hierarchical "is-a" relationship that can also be used by concept-
based overloading, which involves overloading constrained templates on their requirements. As
noted before, you can write a constant-time algorithm for computing the perimeter of an equilateral
polygon, assuming you've already added a suitable multiplication operator to the Numeric concept:
templateP::length_type perimeter(const P& poly) {
return num_sides(poly) * side_length(poly, 0);}
This implementation of perimeter() overloads the previous, Polygon-based implementation. For
types that are non-equilateral Polygons, the previous (linear-time) implementation will be used,
because this (constant-time) constrained template's requirements will not be satisfied. For
equilateral polygons like square, both implementations are valid because every equilateral
polygon is a polygon. In this case, concept-based overloading picks the overload corresponding to
the more refined concept, automatically selecting the constant-time implementation of perimeter().Concept-based overloading (and, related, concept-based partial specialization of class templates)
allows the user to provide multiple, different variations of the same algorithm, which differ only by
the properties of the types that they support, and the compiler will select the most appropriate
algorithm variation for each call site.
Truly Easier C++
Concepts are a significant extension to the C++ template system. By allowing template authors to
explicitly describe the template requirements, the compiler is able to verify both the template
authors and the template user's side of the (previously implicit) template contract. This additional
-
8/8/2019 5147 c++0 Special Report Op
19/42
Easier C++: An Introduction to Concepts
type checking makes it easier to write templates (because they cannot violate the stated contract)
and use templates (because contract violations result in far more readable error messages). The
use of template requirements also enables advanced features that make templates more
reusable, such as the ability to remap syntax within concept maps, and more expressive, through
the use of concept-based overloading.
As of this writing, concepts are expected to be included in the C++0x language and standardlibrary. However, concepts have not yet been voted into the working paper by the ISO C++
standards committee, and some small details may still change. Watch for more interesting
developments in C++0x and the concepts mechanism.
Douglas Gregoris an active member of the ISO C++ committee, where he has lead the
development and specification of various C++09 features, including concepts and variadic
templates. A long-time contributor to Boost, Doug has developed several Boost libraries and acts
as a moderator for the Boost community. Doug is currently the assistant director of the Open
Systems Lab at Indiana University, where his research involves generic programming, parallel
programming, and large-scale graph analysis.
DevX is a division of Jupitermedia Corporation
Copyright 2007 Jupitermedia Corporation. All Rights Reserved. Legal Notices
http://www.internet.com/corporate/legal.htmlhttp://www.internet.com/corporate/legal.html -
8/8/2019 5147 c++0 Special Report Op
20/42
Simpler Multithreading in C++0x
C++0x:The Dawning of a New Standard
-
8/8/2019 5147 c++0 Special Report Op
21/42
Simpler Multithreading in C++0x
Originally from http://www.devx.com/SpecialReports/Article/38883
Simpler Multithreading in C++0xThe new standard will support multithreading, with a new thread library. Find out how thiswill improve porting code, and reduce the number of APIs and syntaxes you use.
by Anthony Williams
ne major new feature in the C++0x standard is multi-threading support. Prior to C++0x, any multi-
threading support in your C++ compiler has been provided as an extension to the C++ standard, which
has meant that the details of that support varies between compilers and platforms. However, with the
new standard, all compilers will have to conform to the same memory model and provide the same
facilities for multi-threading (though implementors are still free to provide additional extensions). What
does this mean for you? It means you'll be able to port multi-threaded code between compilers and
platforms with much reduced cost. This will also reduce the number of different APIs and syntaxes youll
have to know when writing for multiple platforms.
The core of the new thread library is the std::thread class, which manages a thread of execution, so let'sstart by looking at that.
Launching Threads
You start a new thread by constructing an instance of std::thread with a function. This function is then
used as the entry point for the new thread, and once that function returns, the thread is finished:
void do_work();
std::thread t(do_work);
This is just like the thread-creation APIs we're all used tobut there's a crucial difference: This is C++, sowe're not restricted to functions. Just like many of the algorithms in the Standard C++ Library, std::threadwill accept an object of a type that implements the function call operator (operator()), as well as ordinary
functions:
class do_work
{
public:
void operator()();
};
C++Ox:The Dawning of a New Standard
http://www.devx.com/SpecialReports/Article/38883http://www.devx.com/SpecialReports/Article/38883 -
8/8/2019 5147 c++0 Special Report Op
22/42
Simpler Multithreading in C++0x
do_work dw;
std::thread t(dw);
It's important to note that this actually copies the supplied object into the thread. If you really want to use
the object you supplied (in which case, you'd better make sure that it doesn't get destroyed before the
thread finishes), you can do so by wrapping it in std::ref:
do_work dw;
std::thread t(std::ref(dw));
Most thread creation APIs allow you to pass a single parameter to your newly created thread, typically a
long or a void*. std::thread allows arguments too, but you can pass any number, of (almost) any type.
Yes, you read that right: any number of arguments. The constructor uses C++0x's new variadic template
facility to allow a variable number of arguments like the old ... varargs syntax, but in a type-safe manner.
You can now pass objects of any copyable type as arguments to the thread function:
void do_more_work(int i,std::string s,std::vector v);
std::thread
t(do_more_work,42,"hello",std::vector(23,3.141));
Just as with the function object itself, the arguments are copied into the thread before the function is
invoked, so if you want to pass a reference you need to wrap the argument in std::ref:
void foo(std::string&);
std::string s;std::thread t(foo,std::ref(s));
OK, that's enough about launching threads. What about waiting for the thread to finish? The C++
Standard calls that "joining" with the thread (after the POSIX terminology), and you do that with the join()
member function:
void do_work();
std::thread t(do_work);
t.join();
If you're not planning on joining with your thread, just destroy the thread object or call detach():
void do_work();
std::thread t(do_work);
t.detach();
Now, it's very well launching all these threads, but if you're going to share data you'd better protect it. The
new C++ Standard Library provides facilities for that, too.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2242.pdfhttp://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2242.pdfhttp://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2242.pdfhttp://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2242.pdf -
8/8/2019 5147 c++0 Special Report Op
23/42
Simpler Multithreading in C++0x
Protecting Data
In the C++0x thread library, as with most thread APIs, the basic facility for protecting shared data is the
mutex. In C++0x, there are four varieties of mutexes:
non-recursive (std::mutex) recursive (std::recursive_mutex)
non-recursive that allows timeouts on the lock functions (std::timed_mutex)
recursive mutex that allows timeouts on the lock functions (std::recursive_timed_mutex)
All of them provide exclusive ownership for one thread. If you try and lock a non-recursive mutex twice
from the same thread without unlocking in between, you get undefined behavior. A recursive mutex
simply increases the lock countyou must unlock the mutex the same number of times that you locked itinorder for other threads to be allowed to lock the mutex.
Though these mutex types all have member functions for locking and unlocking, in most scenarios the
best way to do it is with the lock class templates std::unique_lock and std::lock_guard. Theseclasses lock the mutex in the constructor and release it in the destructor. Thus, if you use them as local
variables, your mutex is automatically unlocked when you exit the scope:
std::mutex m;
my_class data;
void foo()
{
std::lock_guard lk(m);
process(data);} // mutex unlocked here
std::lock_guard is deliberately basic and can only be used as shown. On the other hand, std::unique_lock
allows for deferred locking, trying to lock, trying to lock with a timeout, and unlocking before the object is
destroyed. If you've chosen to use std::timed_mutex because you want the timeout on the locks, you
probably need to use std::unique_lock:
std::timed_mutex m;
my_class data;
void foo()
{
std::unique_lock
lk(m,std::chrono::milliseconds(3)); // wait up to 3ms
if(lk) // if we got the lock, access the data
process(data);
} // mutex unlocked here
These lock classes are templates, so they can be used with all the standard mutex types, plus any
-
8/8/2019 5147 c++0 Special Report Op
24/42
Simpler Multithreading in C++0x
additional types that supply lock() and unlock() functions.
Protecting Against Deadlock When Locking Multiple Mutexes
Occasionally, an operation requires you to lock more than one mutex. Done wrong, this is a nasty source
of deadlocks: Two threads can try and lock the same mutexes in the opposite order, with each end
upholding one mutex and waiting for the other thread to finish with the other mutexes. The C++0x thread
library allievates this problem, in those cases where you wish to acquire the locks together, by providing
a generic std::lock function that can lock multiple mutexes at once. Rather than calling the lock() member
function on each mutex in turn, you pass them to std::lock(), which locks them all without risking
deadlock. You can even pass in currently unlocked instances of std::unique_lock:
struct X
{
std::mutex m;
int a;
std::string b;
};
void foo(X& a,X& b)
{
std::unique_lock lock_a(a.m,std::defer_lock);
std::unique_lock lock_b(b.m,std::defer_lock);
std::lock(lock_a,lock_b);
// do something with the internals of a and b
}
In the above example, suppose you didn't use std::lock. This could possibly result in a deadlock if onethread did foo(x,y) and another did foo(y,x) for two X objects x and y. With std::lock, this is safe.
Protecting Data During Initialization
If your data only needs protecting during its initialization, using a mutex is not the answer. Doing so only
leads to unnecessary synchronization after initialization is complete. The C++0x standard provides
several ways of dealing with this.
First, suppose your constructor is declared with the new constexpr keyword and satisfies the
requirements for constant initialization. In this case, an object of static storage duration, initialized with
that constructor, is guaranteed to be initialized before any code is run as part of the static initialization
phase. This is the option chosen for std::mutex, because it eliminates the possibility of race conditions
with initialization of mutexes at a global scope:
class my_class
{
int i;
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdfhttp://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf -
8/8/2019 5147 c++0 Special Report Op
25/42
Simpler Multithreading in C++0x
public:
constexpr my_class():i(0){}
my_class(int i_):i(i_){}
void do_stuff();
};
my_class x; // static initialization with constexpr constructor
int foo();
my_class y(42+foo()); // dynamic initialization
void f()
{
y.do_stuff(); // is y initialized?
}
Your second option is to use a static variable at block scope. In C++0x, initialization of block scope static
variables happens the first time the function is called. If a second thread should call the function before
the initialization is complete, then that second thread has to wait:
void bar()
{
static my_class z(42+foo()); // initialization is thread-safe
z.do_stuff();
}
If neither options apply (perhaps because the object is dynamically allocated), then it's best to use std::
call_once and std::once_flag. As the name suggests, when std::call_once is used in conjunction with a
specific instance of type std::once_flag, the specified function is called exactly once:
my_class* p=0;
std::once_flag p_flag;
void create_instance()
{p=new my_class(42+foo());
}
void baz()
{
std::call_once(p_flag,create_instance);
p->do_stuff();
}
-
8/8/2019 5147 c++0 Special Report Op
26/42
Simpler Multithreading in C++0x
Just as with the std::thread constructor, std::call_once can take function objects instead of functions, and
can pass arguments to the function. Again, copying is the default, and you have to use std::ref if you want
a reference.
Waiting for EventsIf you're sharing data between threads, you often need one thread to wait for another to perform some
action, and you want to do this without consuming any CPU time. If a thread is simply waiting for its turn
to access some shared data, then a mutex lock can be sufficient. However, generally doing so won't
have the desired semantics.
The simplest way to wait is to put the thread to sleep for a short period of time. Then check to see if the
desired action has occurred when the thread wakes up. It's important to ensure that the mutex you use to
protect the data indicating that the event has occurred is unlocked whilst the thread is sleeping:
std::mutex m;
bool data_ready;
void process_data();
void foo()
{
std::unique_lock lk(m);
while(!data_ready)
{
lk.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(10));lk.lock();
}
process_data();
}
This method may be simplest, but it's less than ideal for two reasons. Firstly, on average, the thread will
wait five ms (half of ten ms) after the data is ready before it will wake in order to check. This may cause a
noticeable lag in some cases. Though this can be improved by reducing the wait time, it exacerbates the
second problem: the thread has to wake up, acquire the mutex, and check the flag every ten mseven ifnothing has happened. This consumes CPU time and increases contention on the mutex, and thuspotentially slows down the thread performing the task for which it's waiting!
If you find yourself writing code like that, don't: Use condition variables instead. Rather than sleeping for
a fixed period, you can let the thread sleep until it has been notified by another thread. This ensures that
the latency between being notified and the thread waking is as small as the OS will allow, and effectively
reduces the CPU consumption of the waiting thread to zero for the entire time. You can rewrite foo to use
a condition variable like this:
std::mutex m;
-
8/8/2019 5147 c++0 Special Report Op
27/42
Simpler Multithreading in C++0x
std::condition_variable cond;
bool data_ready;
void process_data();
void foo()
{
std::unique_lock lk(m);
while(!data_ready)
{
cond.wait(lk);
}
process_data();
}
Note that the above code passes in the lock object lk as a parameter to wait(). The condition variable
implementation then unlocks the mutex on entry to wait(), and locks it again on exit. This ensures that the
protected data can be modified by other threads whilst this thread is waiting. The code that sets the
data_ready flag then looks like this:
void set_data_ready()
{
std::lock_guard lk(m);
data_ready=true;cond.notify_one();
}
You still need to check that the data is ready though, since condition variables can suffer from what are
called spurious wakes: The call to wait() may return even though it wasn't notified by another thread. If
you're worried about getting this wrong, you can pass that responsibility off to the standard library too, if
you tell it what you're waiting for with a predicate. The new C++0x lambda facility makes this really easy:
void foo()
{std::unique_lock lk(m);
cond.wait(lk,[]{return data_ready;});
process_data();
}
What if you don't want to share your data? What if you want exactly the opposite: For each thread to
have its own copy? This is the scenario addressed by the new thread_local storage duration keyword.
-
8/8/2019 5147 c++0 Special Report Op
28/42
Simpler Multithreading in C++0x
Thread Local Data
The thread_local keyword can be used with any object declaration at namespace scope at local scope,
and specifies that such a variable is thread local. Each thread thus has its own copy of that variable, and
that copy exists for the entire duration of that thread. It is essentially a per-thread static variable, so each
thread's copy of a variable declared at local scope is initialized the first time that particular thread passes
through the declaration, and they retain their values until that thread exits:
std::string foo(std::string const& s2)
{
thread_local std::string s="hello";
s+=s2;
return s;
}
In this function, each thread's copy of s starts life with the contents "hello." Every time the function iscalled, the supplied string is appended to that thread's copy of s. As you can see from this example, this
even works with class types that have constructors and destructors (such as std::string), which is an
improvement over the pre-C++0x compiler extensions.
Thread-local storage isn't the only change to the concurrency support in the core language: There's also
a brand new multi-threading aware memory model, with support for atomic operations.
The New Memory Model and Atomic Operations
Sticking to using locks and condition variables to protect your data, you won't need to worry about the
memory model. The memory model guarantees to protect your data from race conditionsif you use lockscorrectly. You'll get undefined behavior if you don't.
If you're working at a really low-level and providing high-performance library facilities, then it's important
to know the detailswhich are too complicated to go into here. For now, it's enough to know that C++0xhas a set of atomic types corresponding to the built-in integer types and void pointersand a template std::atomicwhich can be used to create an atomic version of a simple user-defined type. You can look upthe relevant documentation for the details.
That's All, Folks!
And thats your whistle-stop tour of the new C++0x threading facilities, which has barely scratched the
surface. There's much more to the library, with features such as thread IDs and asynchronous future
values.
Anthony Williams is the Technical Director forJust Software Solutions Ltd., where he spends most of
his time developing custom software for clients, mostly for Windows, and mostly C++. He is the
maintainer of the Boost Thread library and is also a member of the BSI C++ Standards Panel. His latest
book, 'C++ Concurrency in Action: Practical Multithreading' is currently available in the Early Access
Edition from Manning's web site.
DevX is a division of Jupitermedia Corporation
Copyright 2007 Jupitermedia Corporation. All Rights Reserved. Legal Notices
http://www.open-std.org/JTC1/sc22/wg21/docs/papers/2007/n2427.htmlhttp://www.justsoftwaresolutions.co.uk/http://www.boost.org/http://www.manning.com/williams/http://www.boost.org/http://www.justsoftwaresolutions.co.uk/http://www.open-std.org/JTC1/sc22/wg21/docs/papers/2007/n2427.html -
8/8/2019 5147 c++0 Special Report Op
29/42
The State of the Language:
An Interview with Bjarne Stroustrup
C++0x:The Dawning of a New Standard
-
8/8/2019 5147 c++0 Special Report Op
30/42
The State of the Language: An Interview with Bjarne Stroustrup
Originally from http://www.devx.com/SpecialReports/Article/38813
The State of the Language: An Interview with Bjarne StroustrupC++ founding father assesses the language on the eve of its new standard.
by Danny Kalev
Bjarne Stroustrup, inventor of the C++ programming language, is a computer scientist and the College of Engineering Chair
Professor of Computer Science at Texas A&M University. He has always been highly involved in the standardization of C++.
Since 2004, Bjarne and the standards committee have been busy hammering out the details of a new standard, temporarily
titled C++0x. Bjarne was gracious enough to take time out of his busy schedule to speak with DevX C++ Pro, Danny Kalev,about new C++0x features and the state of the C++ language.
The C++0x standard will be finalized during 2009. Can you outline its major features and its overall importance
to the C++ community?
We hope to vote out a draft standard for public review in October 2008 so that we'll be able to hand in a final draft for
international vote in 2009. Because of this heavy ISO process, it's touch and go whether C++0x will be C++09, but there is
still hope and the major features are now known (barring disasters). We can classify the extensions like this
Concurrency:
memory model supporting modern machine architectures
Threading ABI
atomic types
mutexes and locks
thread local storage
asynchronous message exchange
Libraries:
regex: regular expressions
unordered_map, etc. (hash tables)
smart pointers
array: fixed-sized array
improvements to containers based on new C++0x features
tuples
date and time (maybe)
various library components to held library builders
Language:
rvalue references (move semantics)
static_assert: static assertions
C++Ox:The Dawning of a New Standard
http://www.devx.com/SpecialReports/Article/38813http://www.devx.com/cplus/10%20Minute%20Solution/37436/0http://www.devx.com/cplus/10%20Minute%20Solution/28347/0http://www.devx.com/cplus/10MinuteSolution/21712http://www.devx.com/cplus/10MinuteSolution/21712http://www.devx.com/cplus/10%20Minute%20Solution/28347/0http://www.devx.com/cplus/10%20Minute%20Solution/37436/0http://www.devx.com/SpecialReports/Article/38813 -
8/8/2019 5147 c++0 Special Report Op
31/42
The State of the Language: An Interview with Bjarne Stroustrup
variadic templates
strongly typed enumerations with scoped enumerators
constexpr: generalized constant expressions
control of alignment
delegating and inheriting constructors
auto: deducing a type from an initializer
decltype: a way of using the type of an expression in a declaration
control of defaults
nullptr: a name for the null pointer
a range-based for loop
lambda functions
raw string literals
UTF8 literals
concepts (a type system for template arguments)
initializer lists and uniform initializations syntax and semantics
in-class member initializers
Lists are, by themselves, not very interesting, but you can read up on a description of my general philosophy for language
evolution and some of the individual decisions in my HOPL-iii paper "Evolving a language in and for the real world: C++
1991-2006." You can also find more information than you could possibly want on the committee's web site.
Basically, the "concurrency" features will standardize the basic layers needed to do systems programming in a multi-core
world. Obviously, facilities for doing that already exist in C++ implementations, but they are not standardized. I'd have liked
to see library support for some high-level concurrency models, but the committee didn't have the time or consensus for that.
"I'd have liked to see
library support for some
high-level concurrency
models, but the
committee didn't have
the time or consensus
for that."
The library facilities provide a set of new library components and some improvements to
the existing ones. I would have liked to see many more, but the committee is a volunteer
effort and we just didn't have the resources for a massive extension of what was offered.
Fortunately, there are many libraries available "out there," possibly already on your
machine. For example, many of the C++0x libraries (e.g., regex and unordered_map) are
now shipped by major vendors and boost.org offers many components (for instance, file
system and networking) that we'll probably soon see in the standard. There is also much
talk of a technical report on the libraries we most wanted but had to postpone.
The language extensions are a varied lot. Fortunately most are small and fit together with each other and with existing
facilities to make a better integrated language. Consider a few examples:
// using C++0x features:
vector v = {"Gorm", "Harald", "Sven", "Harald", "Knud" };
for (auto p = v.begin(); p!=v.end(); ++p) cout
-
8/8/2019 5147 c++0 Special Report Op
32/42
The State of the Language: An Interview with Bjarne Stroustrup
making errors and/or for introducing overhead.
Note that all "primitive" features are meant to be used in combination and in combination with existing features to solve
problems. For example, there is no "magic" for initializing vectors in particular or even any new "magic" for constructors.
Instead, the initialization of vector was achieved simply by a rule that state that an initializer_list can be initialized by a list
of any numbers of Ts {t1, t2, t3} for any type T. Given that rule, we simply give vector a constructor that accepts an
initializer_list. Incidentally, this mechanism can also be used to eliminate about 80 percent of the uses of the type-unsafe
stdargs macros.
Concepts are probably the most important addition to C++ in years. Can you please explain which problems in
contemporary C++ concepts will solve?
I suspect that what is most important depends on what you consider important. For many, concepts will be invisible.
They will simply be "the magic" behind a radical improvement of the error messages you get when you make a mistake
using something that happens to be a template. Basically, you'll get the kind of error messages that you are used to from
"ordinary functions" and at the time you get them for mistakes with ordinary functions. What's so great about that? I can
imagine a time a few years into the future where people won't be able to think of a good answer to that question. This will be
the ultimate proof of the success of concepts.
"Concepts" is a type system for types, combinations of types, and combinations of types and integers. Basically, it allows the
programmer to state in code what we currently state in documentation and comments. For example, the standard algorithm
takes a pair of forward iterators and a value; given that, it assigns the value to each element of the sequence defined by the
iterators. That algorithm's requirements on its argument types can be expressed like this:
template
requires Assignable
void fill(For first, For last, const V& v); // just a declaration, not definition
We can try using fill():
int i = 0;
int j = 9;
fill(i, j, 99); // error: int is not a Forward_iterator
int* p= &v[0];
int* q = &v[9];
fill(p, q, 99); // ok: int* is a Forward_iterator
The checking of use happens immediately at the call site and uses only the declaration. This is a major practical advantage
over the current state of affairs for templates where the checking happens lateduring template instantiation time.Concepts also allow us to check the definition of a template in isolations from its arguments:
template
requires Assignable
void fill(For first, For last, const V& v)
{
while (first!=last) {
*first = v;
first=first+1; // error: + not defined for Forward_iterator
// (instead: use ++first)
-
8/8/2019 5147 c++0 Special Report Op
33/42
The State of the Language: An Interview with Bjarne Stroustrup
}
}
Again, the error is immediately caught at the point in the definition where it happens.
This checking is the most obvious benefit of concepts, but they also give greatly improved specification of algorithms and
class templates and makes overloading straightforward, eliminating the complicated scaffolding of traits and helper functions
that people have to rely on today.
Most importantly, concepts help us think about our algorithms and their requirements on their (template) arguments. This
leads to more reliable and more general code. Often, such thinking also leads to improved performance as we are forced to
think about what the algorithm really needs and what is extraneous.
Let's talk about rvalue references. Will they also be "mostly invisible" as concepts, allowing library writers to
implement perfect forwarding and move semantics or will they affect the average C++ programmer's code more
visibly? For instance, will C++ have a fifth canonical member function called a move constructor?
Yes, rvalue references are very much a "technical extension" meant to be invisible to 99.9 percent of programmers.
Their main effect will be some speedup in the implementation of common containers (e.g. vector) and algorithms.
The basic idea behind rvalue references is that we often copy a value when all we wanted was to move it; that is, after a=b,
we have two copies of the value of b, but often the very next thing we do is to destroy b. With rvalue references, we can write
a function move() so that we can write a=move(b) to express that idea. If b is something large (e.g. a string or a matrix), the
performance advantage can be significant (i.e. a factors rather than a small percent). Such move operations will be sprinkled
around in the standard library implementation so that you gain the performance advantage even if you have never heard of
rvalue references.
For example, some standard library classes (e.g. string and vector) will have "move constructor":
T::T(T&&);
which can basically be read as "T doesn't waste time making copies where it can move."
In your recent SD West lecture, you predicted that "concepts are going to be sexy for awhile, then overused
and then people will get sick of it. When things calm down, we'll find out when and where they're actually useful."
Where do you draw the line between valid, welcome usage of concepts as opposed to "overuse"? In other words,
what shouldn't we do with concepts?
I'm pretty sure that was a comment about generic programming in general, rather than about concepts in particular. I'm
hesitant to draw a sharp line. Doing so would imply that I thought I knew what would be right (and not) for essentially all
people in essentially all application areas. I don't, and drawing such lines is also a sign of paternalistic language design. You
can "draw lines" for individuals and organizations (that's what coding standards are for), but not for a set of general language
features for a general-purpose language. The point about "general" is that programmers will discover techniques beyond the
use cases that the language designers thought of. When a new and powerful feature becomes available, it gets overused as
adventurous programmers try to push it to the limit. In the early days of C++, we got inline virtual overloaded functions in
multiply inherited classes. When templates were new, we got excessively clever template meta-programming. This will
happen again with the C++0x feature set. I just hope that this time more people can tell the difference between an
experiment and something ready to put into production code.
Actually, many of the C++0x features, including concepts, are there to ensure that less
-
8/8/2019 5147 c++0 Special Report Op
34/42
The State of the Language: An Interview with Bjarne Stroustrup
"Actually, many of the C+
+0x features, including
concepts, are there to
ensure that less
cleverness is needed to
express interesting
things and to make what
is expressed better
checked."
cleverness is needed to express interesting things and to make what is expressed better
checked. Naturally, that will free up time and energy for even more extreme/interesting
experiments.
What shouldn't we do with concepts? I don't know, but first, I'd use them to precisely and
exhaustively express the requirements of generic algorithms on their (template)
arguments. That should keep me busy for a while. For starters, there is all of the Standard
Library (being done by the committee and in particular by Doug Gregorand friends) and
all of classical mathematics. Once that is done, we should have the experience to do agood job at less regular or more complicated uses of templates. However, please
remember that the aims are simplicity, regularity, and performance. You don't prove that
you are clever by producing the most complicated code.
Is your prediction regarding concepts based on what happened with meta-programming? What is your stance
regarding meta-programming in general? Does this paradigm really offer something indispensable or is it sheer
"cuteness" that has been overstretched?
Partly, but more directly my guess is based on what happened with class hierarchies and overloading. Every powerful
new language feature or programming technique goes through a period of overuse. Later, when understanding is better
diffused in the community, the more reasonable, effective, and maintainable uses become standard practice and the
"interesting" uses get relegated to experimental uses. I think that there are uses of template programming that go beyond
both "classical generic programming" and "cute." I think a first attempt of those would be generative programming where the
template meta-programming techniques are used to generate pretty straightforward code, possibly generic code.
Would it be correct to say that concepts make type traits less needed (or even redundant) in C++0x? Or do type
traits still offer capabilities that concepts don't have?
Concepts can and should eliminate most uses of traits, helper functions for dispatch, and SFINAE overloading.
Basically, the relationship between concepts and traits is that concepts make overloading simple and straightforward without
scaffolding (including traits). For example, assuming a suitable set of concepts we can write:
template
requires Comparable
void sort(I b, I e);
template void sort(C& c) { sort(c.begin(),c.end()); }
template
requires Callable
void sort(C& c, Cmp cmp) { sort(c.begin(),c.end(), cmp); }
Picking the right sort() is trivial:
vector v;
//
sort(v);
sort(v.begin(),v.end());
sort(v,Not_case_sensitive());
There are uses of traits that do not relate to overloading, such as character_traits, which will remain after concepts become
universal. However, most of the prominent uses of traits will be eliminated.
http://www.devx.com/SpecialReports/Article/38864/0/page/1http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_errorhttp://en.wikipedia.org/wiki/Substitution_failure_is_not_an_errorhttp://www.devx.com/SpecialReports/Article/38864/0/page/1 -
8/8/2019 5147 c++0 Special Report Op
35/42
The State of the Language: An Interview with Bjarne Stroustrup
Have you had a chance to program in any of the newer programming languages (Java, C#, Ruby, and so on)?
Do you find in them anything that impresses you or worth commending in terms of novelty, engineering merits, or
simplicity?
I have tried a lot of languages, including those you mention. I'd prefer not to do language comparisons. Suchcomparisons are rarely fair and even less frequently seen to be fair. However, "simple" is not the first word that springs to
mind. I note the size of the run-time support involved. I predicted the growth of Java complexity and don't condemn itIconsider complexity an inevitable consequence of serving a large community. The design aims of those languages are not
those of C++ and vice versa.
I feel that there is pressure to add to C++ features such as finally, garbage collection, and dynamic array
bounds checking as an attempt to appease users of other languages. In reality, these features don't fit well into the
design aims of C++: finally can be replaced by RAII and local classes anyway, GC and destructors are mutually
exclusive, and runtime bounds checking violates the pay-as-you-go and trust-the-programmer principles. Does C++
truly need a GC? And more generally, where do you draw the line between borrowed features that are indispensable
and those that are not?
There is always pressure to add features. Many people think that I and the committee are just bloody-minded and/or
stupid not to immediately add their favorite featuretypically a feature they have tried or just heard of in some other language.Often, those same people complain that the committee is adding too many features and that we should remove some of
those old and ugly "legacy features." Making changes to a widely used language is not easy. There are distinct limits to how
many changes we can add with a reasonable hope that they will be widely useful, rarely harmful, or confusing, and not
breaking existing code. People really don't like their existing code to be broken and making significant extensions 100
percent compatible and properly integrated with all existing and new features can be quite difficult.
I don't know how to draw a sharp line between worthwhile and frivolous extensions, but I do know that no new feature is
really "indispensible." I try to evaluate each new suggested feature on its merits and in the context of existing language
features, library facilities, known problems, and other proposed features. Since the number of new features we can accept islimited, I try to maximize utility, minimize damage, and minimize implementation cost. Each new language feature is an
intricate puzzle and the more fundamental a new feature is, the more parts of the existing language and existing usage are
affected and must be taken into account. For example, the new strongly typed enums were relatively easy to design and
implement because they are an isolated feature, but conversely they are also unlikely to have a dramatic impact on the way
people design their code and view C++. On the other hand, the new facilities for initialization are likely to impact every user
and be highly visible in code. On the other hand, their definition touches many points of current usage and definition and is
therefore at least an order of magnitude harder to design/define than the enumerations.
finally clauses for try blocks are a minor issue andas you mentionredundant in that we can use RAII. It can even be seriouslyargued that providing finally would lead to uglier and more buggy code as programmers used to it in Java (or whatever)
found it easier to avoid learning RAII. finally is not on the list of C++0x features.
The garbage collection issue is not simple. I even think that your question oversimplifies the issueswe need to find a way tocombine GC and destructors. We will not see GC in C++0x, but we will see further work on a design for optional and
programmer-controlled GC. For C++0x, we will get a definition of what it means for a pointer to be disguised and an ABI for
deeming areas of memory "not containing pointers" and/or "cannot be collected." The result of these simple guarantees will
be that existing add-on collectors will be more portable and more effective.
First, let's clarify the ideals: We want simple and comprehensive resource management. That is, we want every resource
acquired to be released ("no leaks"). To get that, we need a programming model that is simple to use. A complicated model
will lead to errors (leaks) when people misuse it or give up on it in favor of (often even more error prone) "home brew"
solutions. "Comprehensive" is essential because memory isn't the only resource we need to worry about; we need to handle
locks, sockets, file handles, thread handles, etc.
-
8/8/2019 5147 c++0 Special Report Op
36/42
The State of the Language: An Interview with Bjarne Stroustrup
We have RAII (for scoped resource use) plus "smart pointers" (for resources that don't have their lifetimes determined by a
simple scope) as a comprehensive solution. From this perspective, the smart pointer types in C++0x completes the RAII
technique supported in C++98. Unfortunately, this set of techniques works only where people use it systematically and
correctly.
For example:
void f(int i, char* p){
vector v(i);
string s(p);
//
}
Here the storage for elements of v and x are handled automatically by the destructors of vector and string. If X has a non-
memory resource (for instance, a lock) vector's destructor will release it. This style of use is simple, widely understood, and
efficient.
"I consider it obviousthat C++ GC will have to
be optional (under some
form of programmer
control) because some
programs do not need
GC (they don't leak),
some programs cannot
afford GC delays (not all
collectors offer real-time
guarantees), and some
programs cannot afford
to include a significant
run-time support system."
The reason that GC is attractive to me is that there are projects where I think that "RAII
plus smart pointers" are unlikely to be systematically and correctly used. Examples areprojects where exceptions cannot be used, projects with a lot of exception-unsafe "legacy"
parts, projects with components developed in a number of places with different
programming philosophies and programmer skills, and projects with long-established
resource management strategies that don't fit the "RAII plus smart pointers" model.
Typically, such projects are valuable, long-lived, expensive to rewrite, and they leak. Add-
on garbage collectors have been successfully used to deal with such leaks for over a
decade. In some cases, the collector is used simply until the leaks can be plugged; in
other cases, they are used because someone gave up plugging all the leaks. This use of
GC is sometimes called "litter collection" as opposed to uses where programs leak for the
convenience of programmers. My guess is that even with the best education based on
RAII, we will have programs that need litter collection "forever"new ones will be written asfast as old ones are made safe.
Note that my aim is not to use GC to hide the problems and complexities of resource
management, but to use GC as yet another tool for dealing with resource problems. This is quite different from the view of
GC as a panacea. The current C++ techniques for resource management deal more directly with the problem than GC and
should be used as the first line of defense against resource management problems. One of the strengths of well-written C++
is exactly that it generates so little garbage. This makes GC in C++ surprisingly (to some) efficient.
I consider it obvious that C++ GC will have to be optional (under some form of programmer control) because some programs
do not need GC (they don't leak), some programs cannot afford GC delays (not all collectors offer real-time guarantees), and
some programs cannot afford to include a significant run-time support system. One of the two major design issues for C++
GC is how to express this programmer control. The difficulty is to ensure that a program component that relies on the
absence of GC (for performance, for instance) is never linked with a component that relies on GC. Remember that there is a
widespread use of dynamic linking and plug-ins so in many contexts whole-program analysis is not an option.
As you indicate, the other big issue is how to reconcile GC and destructors. If programmers come to rely on GC to collect
their garbage for them, they might "leak" an object for the collector to recycle even though that object had a non-trivial
destructora destructor that releases a non-memory resource. For example, given GC, I might use avector v;
Without providing code to delete the pointers when v is destroyed if I "know" that X does not own a non-memory resource.
Such code would be brittle because a change to X (for instance, adding a lock member), could make my code leak. In
general, this problem seems intractable for real-world scenarios involving maintenance of code (adding non-trivial
destructors) and dynamic linking. However, my impression is that a combination of explicit declarations of destructors
-
8/8/2019 5147 c++0 Special Report Op
37/42
The State of the Language: An Interview with Bjarne Stroustrup
releasing non-memory resources ("explicit destructors") and heuristics can eliminate a high percentage of real problems.
Remember that neither of the two "pure" alternatives (no-GC and all-GC) consistently leads to perfect memory management
either (in the hands of real-world programmers for real-world problems), so the absence of a perfect solution should not
deter us from providing a good one.
I know it's a bit early to discuss the farther future of C++ before the C++0x Working Paper is even finalized.
Still, what are the next stages in the standardization of C++ that will take place once C++0x has been ratified?
This question reminds me of coming from an 18-hour flight from Los Angeles into the bright morning light in Sydney to
be met by the question "What do you think of Australia?" That was my first trip down-under so my total experience of
Australia outside the baggage claim area was about two minutes. That's significantly longer than my post-C++0x experience.
We are planning some TR (Technical Reports) and there is some talk about a faster revision cycle (about three years after
the standard). The topics for such TRs (or early revision) are:
Library components:
networking (sockets, iostreams over sockets, etc.)
thread pools
file system
lexical_cast
improved I/O (e.g., faster iostreams, memory-mapped I/O)
special mathematical functions
Language features:
modules (including dynamic libraries)
garbage collection (programmer controlled)
Andas usuala host of little things. I just hope that if the committee takes that path it will not be overwhelmed (distracted) by"little things." As usual, implementations of the library components already exist for experimentation and some (for instance,
the networking library) are already in serious commercial use. In particular, see boost.org.
You've been designing and improving C++, along with other devoted members of the standards committee of
course, for nearly 30 years. I assume that the addition of templates to C++ in the late 1980 proved to be the most
important and successful feature that C++ ever got since you added classes to C in 1979. Is this assumption
correct? Is there any C++ feature you regret?
There are things that I would have liked to do better, but who am I to second guess the younger Bjarne? I am no
smarter than him and he had a better understanding of the conditions at the time.
In the case of templates, I knew their strengths and I knew their greatest weakness at the time. The first three pages in D&E
is a lament for not solving the template argument checking problem. In 1988, I knew the problem, but I don't think anyone
knew a solution that would have been viable in the context of the Draconian requirements of flexibility, generality, and
performance that were (and are) the bedrock of template use. The design of concepts involved solving genuine research
problems; we have papers in POPL and OOPSLA to prove that!
C compatibility has always been a serious problem. The C syntax is horrendous, the conversion rules chaotic, and arrays
decay to pointers at the slightest excuse.
http://www.boost.org/http://www.boost.org/ -
8/8/2019 5147 c++0 Special Report Op
38/42
The State of the Language: An Interview with Bjarne Stroustrup
These are fundamental and constant problems. However, I chose C compatibility (though never 100 percent compatibility)
as a means of making C++ a practical tool rather than yet another pretty language and so we had to live with it. Even today,
the overlap between the C and C++ communities and code bases are so large that a serious break of compatibility would
simply lead to the language community fragmenting. We have to proceed in the usual way: provide superior alternatives to
ugly and/or dangerous features and hope that people use them.
In this respect, I read a Slashdot interview in which you say that you would have anyway started off with an
existing programming language as the basis for a new one, as opposed to starting from scratch. Why not start fromscratch, really? And why start with C?
Again, I don't have a time machine. At the time, C looked like a good choice for me even though C was not an obvious
choice then. My guess is that most people would have chosen something simpler and cleaner, such as Pascal or Modula,
over the flexibility and performance of C.
Why not start from scratch? Well, at the time, I decided that to build a useful tool with the skills and resources available,
building on an existing language was essential. I wanted a tool, not a beautiful toy. If I had to do a new language, I would
again have to evaluate my aims and my resources. Starting from scratch is hard. We always carry our experience with us
and anything sufficiently different from the familiar will cause teaching problems. People invariably confuse familiarity with
simplicity. Even Java, which was supposedly designed from scratch according to first principles, chose the ugly and illogical
C syntax to appeal to C and C++ programmersand Java doesn't even have the excuse of compatibility. Should I designanother language, I guess I would have to try "from scratch" just to get a new set of problems. In that case, I would be very
sensitive to the idea that there are other forms of compatibility than source code compatibility, such as link compatibility and
source code transformation.
Is C++ usage really declining, as some biased analysts and journalists have been trying to convince us for
years (often not without ulterior motives), or is this complete nonsense?
It's probably not complete nonsense. C++ use appears to be declining in some areas and appears to be on an upswing
in other areas. If I had to guess, I'd suspect a net decrease sometime during 2002-2004 and a net increase in 2005-2007,
but I doubt anyone really knows. Most of the popular measures basically measures noise and ought to report their findings in
decibel rather than "popularity." Many of the major uses are in infrastructure (telecommunications, banking, embedded
systems, etc.) where programmers don't go to conferences or describe their code in public. Many of the most interesting and
important C++ applications are not noticed, they are not for sale to the public as programming products, and their
implementation language is never mentioned. Examples are Google and "800" phone numbers. Had I thought of a "C++
inside" logo in 1985, the programming world might have been different today.
Among the positive signs for C++, I can mention an increase in my email and the number of speaking invitations. At the
SDWest conference in March, the C++ track was by far the largest, even without counting the "Super tutorial" that I gave
with Herb Sutter; more than 300 people attended that oneup by a factor of two compared to two years ago. These are, ofcourse, personal and subjective measures, but they fit with other information that I get from innumerable sources in the
industry (worldwide).
"We don't know what the
challenges will be and
that's C++'s greatest
strength."
It's a really big world "out there" and the increase in the number of users of one language
does not imply the decrease in the numbers of another. I suspect we have reached the
point where if you can count your users to the nearest million, you don't count as a major
language. Similarly, I got into a discussion with some friends about how many billions of
lines of C++ code there were "out there." We concluded that we did not know, but it didn't
require much thought to demonstrate that the plural was appropriate.
One simple thing that confuses many discussions of language use/popularity is the distinction between relative and absolute
measures. For example, I say that C++ use is growing when I see user population grow by 200,000 programmers from 3.1M
to 3.3M. However, somebody else may claim that "C++ is dying" because it's "popularity" has dropped from 16 percent to 10
-
8/8/2019 5147 c++0 Special Report Op
39/42
-
8/8/2019 5147 c++0 Special Report Op
40/42
Timeline: C++ in Retrospect
C++0x:The Dawning of a New Standard
-
8/8/2019 5147 c++0 Special Report Op
41/42
Timeline: C+ + in R