Sutter's Mill
GotW #4: Class Mechanics (7/10)
How good are you at the details of writing classes? This item focuses not only on blatant errors, but even more so on professional style. Understanding these principles will help you to design classes that are easier to use and easier to maintain.
Problem
JG Question
1. What makes interfaces “easy to use correctly, hard to use incorrectly”? Explain.
Guru Question
2. You are doing a code review. A programmer has written the following class, which shows some poor style and has some real errors. How many can you find, and how would you fix them?
class complex {
public:
complex( double r, double i = 0 )
: real(r), imag(i)
{ }
void operator+ ( complex other ) {
real = real + other.real;
imag = imag + other.imag;
}
void operator<<( ostream os ) {
os << "(" << real << "," << imag << ")";
}
complex operator++() {
++real;
return *this;
}
complex operator++( int ) {
auto temp = *this;
++real;
return temp;
}
// ... more functions that complement the above ...
private:
double real, imag;
};
Note: This is not intended to be a complete class. For example, if you provide operator++ you would normally also provide operator–. Rather, this is an instructive example to focus on the mechanics of writing correctly the kinds of functions this class is trying to support.
Filed under: GotW
Read More »
GotW #3 Solution: Using the Standard Library (or, Temporaries Revisited)
Effective reuse is an important part of good software engineering. To demonstrate how much better off you can be by using standard library algorithms instead of handcrafting your own, let’s reconsider the previous question to demonstrate how many of the problems could have been avoided by simply reusing what’s already available in the standard library.
Problem
JG Question
1. What is the most widely used C++ library?
Guru Question
2. How many of the pitfalls in GotW #2 could have been avoided in the first place, if only the programmer had replaced the explicit iterator-based for loop with:
(a) a range-based for loop?
(b) a standard library algorithm call?
Demonstrate. (Note: As with GotW #2, don’t change the semantics of the function, even though they could be improved.)
To recap, here is the mostly-fixed function:
string find_addr( const list<employee>& emps, const string& name ) {
for( auto i = begin(emps); i != end(emps); ++i ) {
if( i->name() == name ) {
return i->addr;
}
}
return "";
}
Solution
1. What is the most widely used C++ library?
The C++ standard library, with its implementations on every platform.
2. (a) How many of the pitfalls in GotW #2 could have been avoided with a range-based for loop?
Astute readers of GotW #2 will have been champing at the bit to say: “Why aren’t you using a range-based for loop?” Indeed, why not? That would solve several of the temporaries, never mind be easier to write.
Compare the original unimproved explicit iterator loop:
for( auto i = begin(emps); i != end(emps); i++ ) {
if( *i == name ) {
return i->addr;
}
}
with the range-based for loop (bonus points if you remembered to write the const auto&):
for( const auto& e : emps ) {
if( e == name ) {
return e.addr;
}
}
The expressions e == name and return e.addr; are unchanged in terms of their possible or actual temporaries. But the questions in the naked loop code about whether or not the = causes a temporary (recall: it doesn’t), whether or not end() recalculation matters and should be hoisted (recall: probably not, but maybe), and whether or not i++ should be rewritten ++i (recall: it should) all simply don’t arise in the range-for code. Such is the power of clear code, and using a higher level of abstraction.
A key advantage is that using the range-based for loop has increased our level of abstraction, the information density in our code. Consider: What can you say about the following two pieces of code without reading what comes next?
for( auto i = begin(emps); i != end(emps); i++ ) { // A
for( const auto& e : emps ) { // B
At first it might seem that lines A and B convey the same information, but they don’t. When you see A, all you know is that there’s a loop of some sort that uses an iterator over emps. Granted, we’re so used to A that our eye’s peripheral vision tends to “autocomplete” it in our heads into “a loop that visits the elements of emps in order” and our autocomplete is often correct—except when it isn’t: was that a ++, or a s+= 2 in a strided loop? is the index modified inside the body? Our peripheral vision might be wrong.
On the other hand, B conveys more information to the reader. When you see B, you know for certain without inspecting the body of the loop that it is a loop that visits the element of emps in order. What’s more, you’ve simplified the loop control because there’s no need for an iterator indirection. Both of these are raising the level of abstraction of our code, and that’s a good thing.
Note that, as discussed in GotW #2, the naked for loop didn’t naturally allow consolidating to a single return statement without resorting to making the code more complex by adding an additional variable and performing extra computation (a default construction followed by an assignment, instead of just a construction). That’s still true of the range-based for loop form, because it still has the two return statements in different scopes.
2. (b) … with a standard library algorithm call?
With no other changes, simply using the standard find algorithm could do everything the range-based for loop did to avoid needless temporaries (and questions about them):
// Better (focusing on internals)
//
string find_addr( /*...*/ ) {
const auto i = find( begin(emps), end(emps), name ); // TFTFY
return i != end(emps) ? i->addr : "";
}
This naturally eliminates the same temporaries as the range-for version, and it further increases our level of abstraction. As with the range-based for loop, we can see at a glance and for certain that the loop will visit the elements of emps in order, but on top of that we also know we’re trying to find something and will get back an iterator to the first matching element if one exists. We do still have an iterator indirection, but only a single-use iterator object and no iterator arithmetic as in the original naked iterator for loop.
Further, we have eliminated a loop nested scope entirely and flattened out the function to a single scope which can simplify this calling function in ways even the range-for couldn’t. To demonstrate still more just how fundamental this point is, note that what else the flattening out of the body buys us: Now, because the return statements are in the same scope (possible only because we eliminated the loop scope), we have the option of naturally combining them. You could still write if( i != end(emps) ) return i->addr; else return “”; here, on one or two or four lines, but there’s no need to. To be clear, the point here is not that reducing return statements should be a goal in itself—it shouldn’t be, and “single exit” thinking has always been flawed as we already saw in GotW #2. Rather, the point is that using an algorithm often simplifies our code more than an explicit loop, even a range-for loop, can do—not only directly by removing extra indirections and extra variables and a loop nested scope, but often also by permitting additional simplifications in nearby code.
The above code might still cause a temporary when comparing an employee with a string, and we can eliminate even that temporary if we go one small step further and use find_if with a custom comparison that compares e.name() == name to avoid a possible conversion, assuming something like a suitable employee::name() is available as we did in GotW #2. Combining this with the other fixes to pass parameters by reference, we get:
// Better still (complete)
//
string find_addr( const list<employee>& emps, const string& name ) {
const auto i = find_if( begin(emps), end(emps),
[&](const auto& e) { return e.name() == name; } );
return i != end(emps) ? i->addr : "";
}
Summary
Prefer algorithm calls over explicit loops, when you have or can write a suitable algorithm that does what you want. They raise the level of abstraction and the clarity of our code. Scott Meyers’ advice in Effective STL is still true, and more applicable than even now that lambdas make algorithms much more usable than before:
Guideline: Prefer algorithm calls to explicit loops. Algorithm calls are often clearer and reduce complexity. If no suitable algorithm exists, why not write it? You’ll use it again.
Prefer reusing existing library code to handcrafting your own. The more widely used the library, the more likely it is to come well-designed, pre-debugged, and pre-optimized for many common requirements. And what library is more widely used than the standard library? In your C++ program, your standard library implementation is the most widely used library code you’re likely to use. This helps you both in the library’s design and its implementation: It’s full of code that’s intended to be used and reused, and to that end a lot of thought and care has gone into the design of its features, including its standard algorithms like find and sort. Implementers have also spent hours sweating over efficiency details, and usability details, and all sorts of other considerations so that you don’t have to—including performing optimizations you should almost never resort to in application-level code, such as using nonportable OS- and CPU-target specific optimizations.
So, always prefer to reuse code, especially algorithms and especially the standard library, and escape the trap of “I’ll-write-my-own-just-’cause-I-can.”
Guideline: Reuse code—especially standard library code—instead of handcrafting your own. It’s faster, easier, and safer.
Acknowledgments
Thanks in particular to the following for their feedback to improve this article: Olaf ven der Spek, Sam Kramer.
Filed under: GotW
Read More »
GotW #3: Using the Standard Library (or, Temporaries Revisited) (3/10)
Effective reuse is an important part of good software engineering. To demonstrate how much better off you can be by using standard library algorithms instead of handcrafting your own, let’s reconsider the previous question to demonstrate how many of the problems could have been avoided by simply reusing what’s already available in the standard library.
Problem
JG Question
1. What is the most widely used C++ library?
Guru Question
2. How many of the pitfalls in GotW #2 could have been avoided in the first place, if only the programmer had replaced the explicit iterator-based for loop with:
(a) a range-based for loop?
(b) a standard library algorithm call?
Demonstrate. (Note: As with GotW #2, don’t change the semantics of the function, even though they could be improved.)
To recap, here is the mostly-fixed function:
string find_addr( const list<employee>& emps, const string& name ) {
for( auto i = begin(emps); i != end(emps); ++i ) {
if( i->name() == name ) {
return i->addr;
}
}
return "";
}
Filed under: GotW
Read More »
GotW #2 Solution: Temporary Objects
Unnecessary and/or temporary objects are frequent culprits that can throw all your hard work—and your program’s performance—right out the window. How can you spot them and avoid them?
Problem
JG Question
1. What is a temporary object?
Guru Question
2. You are doing a code review. A programmer has written the following function, which uses unnecessary temporary or extra objects in at least three places. How many can you identify, and how should the programmer fix them?
string find_addr( list<employee> emps, string name ) {
for( auto i = begin(emps); i != end(emps); i++ ) {
if( *i == name ) {
return i->addr;
}
}
return "";
}
Do not change the operational semantics of this function, even though they could be improved.
Solution
1. What is a temporary object?
Informally, a temporary object is an unnamed object that you can’t take the address of. A temporary is often created as an intermediate value during the evaluation of an expression, such as an object created by returning a value from a function, performing an implicit conversion, or throwing an exception. We usually call a temporary object an “rvalue,” so named because it can appear on the “r”ight hand side of an assignment. Here are some simple examples:
widget f(); // f returns a temporary widget object
auto a = 0, b = 1;
auto c = a + b; // "a+b" creates a temporary int object
In contrast, in the same code we have objects like a and c that do each have a name and a memory address. Such an object is usually called an “lvalue,” because it can appear on the “l”eft hand side of an assignment.
That’s a simplification of the truth, but it’s generally all you need to know. More precisely, C++ now has five categories of values, but distinguishing them is primarily useful for writing down the language specification, and you can mostly ignore them and just think about “rvalues” for temporary objects without names and whose addresses can’t be taken, and “lvalues” for non-temporary objects that have names and whose addresses can be taken.
2. How many unnecessary temporary objects can you identify, and how should the programmer fix them?
Believe it or not, this short function harbors three obvious cases of unnecessary temporaries or extra copies of objects, two subtler ones, and three red herrings.
The parameters are passed by value.
The most obvious extra copies are buried in the function signature itself:
string find_addr( list<employee> emps, string name )
The parameters should be passed by const&—that is, const list<employee>& and const string&, respectively—instead of by value. Pass-by-value forces the compiler to make complete copy of both objects, which can be expensive and, here, is completely unnecessary.
Guideline: Prefer passing read-only parameters by const& instead of by value.
Pedantic note: Yes, with pass-by-value, if the caller passed a temporary list or string argument then it could be moved from rather than copied. But I’m deliberately saying “forces the compiler to make a complete copy” here because no caller is realistically going to be passing a temporary list to find_addr, except by mistake.
Non-issue: Initializing with “=”.
Next we come to the first red herring, in the for loop’s initialization:
for( auto i = begin(emps); /*...*/ )
You might be tempted to say that this code should prefer to be spelled auto i(begin(emps)) rather than auto i = begin(emps), on the grounds that the = syntax incurs an extra temporary object, even if it might be optimized away. After all, as we saw in GotW #1, usually that extra = means the two-step “convert to a temporary then copy/move” of copy-initialization—but recall that doesn’t apply when using auto like this. Why?
Remember that auto always deduces the exact type of the initializer expression, minus top-level const and & which don’t matter for conversions, and so… presto! there cannot be any need for a conversion and we directly construct i.
So there is no difference between auto i(begin(emps)) and auto i = begin(emps). Which syntax you choose is up to you, but it depends only on taste, not on temporaries or any other performance or semantic difference.
Guideline: Prefer declaring variables using auto. Among other reasons to do so, it naturally guarantees zero extra temporaries due to implicit conversions.
The end of the range is recalculated on each loop iteration.
Another potential avoidable temporary occurs in the for loop’s termination condition:
for( /*...*/ ; i != end(emps); /*...*/ )
For most containers, including list, calling end() returns a temporary object that must be constructed and destroyed, even though the value will not change.
Normally when a value will not change, instead of recomputing it (and reconstructing and redestroying it) on every loop iteration, we would want to compute the value only once, store it in a local object, and reuse it.
Guideline: Prefer precomputing values that won’t change, instead of recreating objects unnecessarily.
However, a caution is in order: In practice, for simple inline functions like list<T>::end() in particular used in a loop, compilers routinely notice their values won’t change and hoist them out of the loop for you without you having to do it yourself. So I actually don’t recommend any change to hoist the end calculation here, because that would make the code slightly more complex and the definition of premature optimization is making the code more complex in the name of efficiency without data that it’s actually needed. Clarity comes first:
Definition: Premature optimization is when you make code more complex in the name of efficiency without data that it’s actually needed.
Guideline: Write for clarity and correctness first. Don’t optimize prematurely, before you have profiler data proving the optimization is needed, especially in the case of calls to simple inline calls to short functions that compilers normally can handle for you.
The iterator increment uses postincrement.
Next, consider the way we increment i in the for loop:
for( /*...*/ ; i++ )
This temporary is more subtle, but it’s easy to understand once you remember how preincrement and postincrement differ. Postincrement is usually less efficient than preincrement because it has to remember and return its original value.
Postincrement for a class T should normally be implemented using the canonical form as follows:
T T::operator++(int)() {
auto old = *this; // remember our original value
++*this; // always implement postincr in terms of preincr
return old; // return our original value
}
Now it’s easy to see why postincrement is less efficient than preincrement: Postincrement has to do all the same work as preincrement, but in addition it also has to construct and return another object containing the original value.
Guideline: For consistency, always implement postincrement in terms of preincrement, otherwise your users will get surprising (and often unpleasant) results.
In the problem’s code, the original value is never used, and so there’s no reason to use postincrement. Preincrement should be used instead. Although the difference is unlikely to matter for a built-in type or a simple iterator type, where the compiler can often optimize away the extra unneeded work for you, it’s still a good habit not to ask for more than you need.
Guideline: Prefer preincrement. Only use postincrement if you’re going to use the original value.
“But wait, you’re being inconsistent!” I can just hear someone saying. “That’s premature optimization. You said that compilers can hoist the end() call out of the loop, and it’s just as easy for a compiler to optimize away this postincrement temporary.”
That’s true, but it doesn’t imply premature optimization. Preferring ++i does not mean writing more complex code in the name of performance before you can prove it’s needed—++i is not more complex than i++, so it’s not as if you need performance data to justify using it! Rather, preferring ++i is avoiding premature pessimization, which means avoiding writing equivalently complex code that needlessly asks for extra work that it’s just going to ignore anyway.
Definition: Premature pessimization is when you write code that is slower than it needs to be, usually by asking for unnecessary extra work, when equivalently complex code would be faster and should just naturally flow out of your fingers.
The comparison might use an implicit conversion.
Next, we come to this:
if( *i == name )
The employee class isn’t shown in the problem, but we can deduce a few things about it. For this code to work, employee likely must either have a conversion to string or a conversion constructor taking a string. Both cases create a temporary object, invoking either operator== for strings or operator== for employees. (Only if there does happen to be an operator== that takes one of each, or employee has a conversion to a reference, that is, string&, is a temporary not needed.)
Guideline: Watch out for hidden temporaries created by implicit conversions. One good way to avoid this is to make constructors and conversion operators explicit by default unless implicit conversions are really desirable.
Probably a non-issue: return “”.
return "";
Here we unavoidably create a temporary (unless we change the return type, but we shouldn’t; see below), but the question is: Is there a better way?
As written, return “”; calls the string constructor that takes a const char*, and if the string implementation you’re using either (a) is smart enough to check for the case where it’s being passed an empty string, or (b) uses the small string optimization (SSO) that stores strings up to a certain size directly within the string object instead of on the heap, no heap allocation will happen.
Indeed, every string implementation I checked is smart enough not to perform an allocation here, which is maximally efficient for string, and so in practice there’s nothing to optimize. But what alternatives do we have? Let’s consider two.
First, you might consider re-spelling this as return “”s; which is new in C++14. That essentially relies on the same implementation smarts to check for empty strings or to use SSO, just in a different function—the literal operator”".
Second, you might consider re-spelling this as return { };. On implementations that are both non-smart and non-SSO, this might have a slight advantage over the others because it invokes the default constructor, and so even the most naïve implementation is likely not to do an allocation since clearly no value is needed.
In summary, there’s no difference in practice among returning “”, “”s, or { }; use whichever you prefer for stylistic reasons. If your string implementation is either smart or uses SSO, which covers all implementations I know of, there’s exactly zero allocation difference.
Note: SSO is a wonderful optimization for avoiding allocation overhead and contention, and every modern string ought to use it. If your string implementation doesn’t use SSO (as of this writing, I’m looking at you, libstdc++), write to your standard library implementer—it really should.
Non-issue: Multiple returns.
return i->addr;
return "";
This was a second subtle red herring, designed to lure in errant disciples of the “single-entry/single-exit” (SE/SE) persuasion.
I In the past, I’ve heard some people argue that it’s better to declare a local string object to hold the return value and have a single return statement that returns that string, such as writing string ret; … ret = i->addr; break; … return ret;. The idea, they say, is that this will assist the optimizer perform the ‘named return value optimization.’
The truth is that whether single-return will improve or degrade performance can depend greatly on your actual code and compiler. In this case, the problem is that creating a single local string object and then assigning it would mean calling string’s default constructor and then possibly its assignment operator, instead of just a single constructor as in our original code. “But,” you ask, “how expensive could a plain old string default constructor be?” Well, here’s how the “two-return” version performed on one popular compiler last time I tried it:
with optimizations disabled: two-return 5% faster than a “return value” string object
with aggressive optimizations: two-return 40% faster than a “return value” string object
Note what this means: Not only did the single-return version generate slower code on this particular compiler on this particular day, but the slowdown was greater with optimizations turned on. In other words, a single-return version didn’t assist optimization, but actively interfered with it by making the code more complex.
In general, note that SE/SE is an obsolete idea and has always been wrong. “Single entry,” or the idea that functions should always be entered in one place (at their start) and not with goto jumps from the caller’s code directly to random places inside the function body, was and is an immensely valuable advance in computer science. It’s what made libraries possible, because it meant you could package up a function and reuse it and the function would always know its starting state, where it begins, regardless of the calling code. “Single exit,” on the other hand, got unfairly popular on the basis of optimization (‘if there’s a single return the compiler can perform return value optimization better’—see counterexample above) and symmetry (‘if single entry is good, single exit must be good too’) but that is wrong because the reasons don’t hold in reverse—allowing a caller to jump in is bad because it’s not under the function’s control, but allowing the function itself to return early when it knows it’s done is perfectly fine and fully under the function’s control. To put the final nail in the coffin, note that “single exit” has always been a fiction in any language that has exceptions, because you can get an early exceptional return from any point where you call something that could throw an exception.
Non-issue: Return by value.
Which brings us to the third red herring:
string find_addr( /*...*/ )
Because C++ naturally enables move semantics for returned values like this string object, there’s usually little to be gained by trying to avoid the temporary when you return by value. For example, if the caller writes auto address = find_addr( mylist, “Marvin the Robot” );, there will be at most a cheap move (not a deep copy) of the returned temporary into address, and compilers are allowed to optimize away even that cheap move and construct the result into address directly.
But what if you did feel tempted to try to avoid a temporary in all return cases by returning a string& instead of string? Here’s one way you might try doing it that avoids the pitfall of returning a dangling reference to a local or temporary object:
const string& find_addr( /* ... */ ) {
for( /* ... */ ) {
if( /* found */ ) {
return i->addr;
}
}
static const string empty;
return empty;
}
To demonstrate why this is brittle, here’s an extra question:
For the above function, write the documentation for how long the returned reference is valid.
Go ahead, we’ll wait.
Done? Okay, let’s consider: If the object is found, we are returning a reference to a string inside an employee object inside the list, and so the reference itself is only valid for the lifetime of said employee object inside the list. So we might try something like this (assuming an empty address is not valid for any employee):
“If the returned string is nonempty, then the reference is valid until the next time you modify the employee object for which this is the address, including if you remove that employee from the list.”
Those are very brittle semantics, not least because the first (but far from only) problem that immediately arises is that the caller has no idea which employee that is—not only doesn’t he have a pointer or reference to the right employee object, but he may not even be able to easily figure out which one it is if two employees could have the same address. Second, calling code can be notoriously forgetful and careless about the lifetimes of the returned reference, as in the following code which compiles just fine:
auto& a = find_addr( emps, "John Doe" ); // yay, avoided temporary!
emps.clear();
cout << a; // oops
When the calling code does something like this and uses a reference beyond its lifetime, the bug will typically be intermittent and very difficult to diagnose. Indeed, one of the most common mistakes programmers make with the standard library is to use iterators after they are no longer valid, which is pretty much the same thing as using a reference beyond its lifetime; see GotW #18 for details about the accidental use of invalid iterators.
Summary
There are some other optimization opportunities. Ignoring these for now, here is one possible corrected version of find_addr which fixes the unnecessary temporaries. To avoid a possible conversion in the employee/string comparison, we’ll assume there’s something like an employee::name() function and that .name() == name has equivalent semantics.
Note another reason to prefer declaring local variables with auto: Because the list<employee> parameter is now const, calling begin and end return a different type—not iterators but const_iterators—but auto naturally deduces the right thing so you don’t have to remember to make that change in your code.
string find_addr( const list<employee>& emps, const string& name ) {
for( auto i = begin(emps); i != end(emps); ++i ) {
if( i->name() == name ) {
return i->addr;
}
}
return "";
}
Acknowledgments
Thanks in particular to the following for their feedback to improve this article: “litb1,” Daan Nusman, “Adrian,” Michael Marcin, Ville Voutilainen, Rick Yorgason, “kkoehne,” and Olaf van der Spek.
Filed under: GotW
Read More »
GotW #2: Temporary Objects
Unnecessary and/or temporary objects are frequent culprits that can throw all your hard work — and your program’s performance — right out the window. How can you spot them and avoid them?
Problem
JG Question
1. What is a temporary object?
Guru Question
2. You are doing a code review. A programmer has written the following function, which uses unnecessary temporary objects in at least three places. How many can you identify, and how should the programmer fix them?
string find_addr( list<employee> emps, string name ) {
for( auto i = begin(emps); i != end(emps); i++ ) {
if( *i == name ) {
return i->addr;
}
}
return "";
}
Do not change the operational semantics of this function, even though they could be improved.
Filed under: GotW
Read More »
GotW #1 Solution: Variable Initialization – or Is It?
This first problem highlights the importance of understanding what you write. Here we have a few simple lines of code—most of which mean something different from all the others, even though the syntax varies only slightly.
Problem
JG Question
1. What is the difference, if any, among the following?
widget w; // (a)
widget w(); // (b)
widget w{}; // (c)
widget w(x); // (d)
widget w{x}; // (e)
widget w = x; // (f)
widget w = {x}; // (g)
auto w = x; // (h)
auto w = widget{x}; // (i)
Guru Questions
2. What do each of the following lines do?
vector<int> v1( 10, 20 ); // (a)
vector<int> v2{ 10, 20 }; // (b)
3. Besides the cases above, what other benefits are there to using { } to initialize objects?
4. When should you use ( ) vs. { } syntax to initialize objects? Why?
Solution
This puzzle demonstrates several things:
The difference between default initialization, direct initialization, copy initialization, and list initialization.
The difference between using ( ) and using { } for initialization.
A red herring that isn’t initialization at all, and which modern C++ entirely avoids.
But, most important of all: If you stick to two simple Guidelines, which we’ll cover in #4, you can ignore most of these cases and the rules are pretty simple and deliver efficient performance by default.
1. What is the difference, if any, among the following?
Let’s consider the cases one by one.
(a) is default initialization.
widget w; // (a)
This code declares a variable named w, of type widget. For most types, it is initialized using the default constructor widget::widget().
Note that w is not initialized and contains garbage values if widget happens to be a built-in type like int, or a simple “int-like” class type with what’s called a “trivial” default constructor—a type that relies on the compiler-generated default constructor, has no virtual functions or virtual base classes or data member initializers, and all its bases and members satisfy the same restrictions.
(b) is a “vexing” red herring, now mostly a historical curiosity.
widget w(); // (b)
This is a pre-modern C++ pitfall: At first glance, it may look like just another variable declaration calling a default constructor widget::widget(); in reality, thanks to a grammar ambiguity, it’s a function declaration for a function named w that takes no parameters and returns a widget object by value. (If you can’t see this at first, consider that the above code is no different from writing something like int f(); which is clearly a function declaration.)
Lest you think “aw, but those ( ) parentheses are redundant, it’s the programmer’s own fault for not just writing widget w; there!”, note that the same problem arises in those occasional cases where you think you’re initializing a variable with temporary objects:
// same problem (gadget and doodad are types)
//
widget w( gadget(), doodad() ); // pitfall: not a variable declaration
Scott Meyers long ago named this “C++’s most vexing parse,” because the standard resolves the parsing ambiguity by saying: “if it can be a function declaration, it is.”
The good news is that this is now mostly a historical curiosity, not something you should encounter in new code, because C++11 removed this pitfall. Note that C++11 does not change the meaning of the code—C++11 has great backward compatibility with C++98, including that this ambiguity still has the same meaning it always had. Rather, C++11 solves this by providing a syntax that supersedes case (b) in nearly all cases, so that we don’t need to ever fall into this pit anymore:
(c) is non-vexing and clear.
widget w{}; // (c)
Here we have the first reason to prefer { } to ( ): For any class type widget, line (c) does the “best parts” of (a) and (b)—it always initializes the variable, and is never ambiguous with a function declaration. No vex, no fuss, no muss.
“Aha, but wait, it’s not that simple!” someone might object. “What if widget has a constructor that takes a std::initializer_list? Those are greedy (preferred), so if widget has one of those wouldn’t this call that?”
The answer is no, this really is just as simple as it looks, because the standard is explicit that an empty { } list means to call the default constructor if available. However, it’s good to be aware of initializer_lists, so let’s talk about those next.
(d) and (e) are direct initialization.
Now let’s consider cases where we actually initialize w from some existing variable:
widget w(x); // (d)
widget w{x}; // (e)
Assuming x is not the name of a type, these are both direct initialization. That’s because the variable w is initialized “directly” from the value of x by calling widget::widget(x). If x is also of type widget, this invokes the copy constructor. Otherwise, it invokes a converting constructor.
However, note that the syntax {x} creates an initializer_list. If widget has a constructor that takes an initializer_list, that constructor is preferred; otherwise, if widget has a constructor that takes whatever type x is (possibly with conversions), that constructor is used.
There are two major differences that make (e) superior to (d): First, like (c), syntax (e) is unambiguous and avoids the vexing parse. If x is a type name, then (d) is a function declaration even if there is also a variable named x in scope (see above), whereas (e) is never a function declaration.
Second, syntax (e) is safer because it does not allow narrowing (a.k.a. “lossy”) conversions that are otherwise allowed for some built-in types. Consider:
int i1( 12.345 ); // ok: toss .345, we didn't like it anyway
int i2{ 12.345 }; // error: would be lossy implicit narrowing
(f) and (g) are copy initialization and copy list initialization.
This brings us to our final two non-auto cases:
widget w = x; // (f)
This is called “copy initialization.” Conceptually, the variable w is initialized using widget‘s move or copy constructor, possibly after calling another function to convert the argument implicitly (explicit conversions won’t be invoked here).
Common Mistake: This is always initialization; it is never assignment, and so it never calls T::operator=(). Yes, I know there’s an “=” character in there, but don’t let that throw you — that’s just a syntax holdover from C, not an assignment operation.
Here are the semantics:
If x is of type widget, line (f) means the same as (d) widget w(x); except that explicit constructors cannot be used. It’s guaranteed that only a single constructor is called.
If x is of some other type, conceptually the compiler first implicitly converts x to a temporary widget object, then move-constructs w from that temporary rvalue, using copy construction as “the slow way to move” as a backup if no better move constructor is available. Assuming that an implicit conversion is available, (f) means the same as widget w( widget(x) );.
Note that I said “conceptually” a few times above. That’s because practically compilers are allowed to, and routinely do, optimize away the temporary and, if an implicit conversion is available, convert (f) to (d), thus optimizing away the extra move operation. However, even when the compiler does this, the widget copy constructor must still be accessible, even if is not called—the copy constructor’s side effects may or may not happen, that’s all.
Now note the related syntax that adds “=“:
widget w = {x}; // (g)
This is called “copy list initialization.” It means the same as widget w{x}; except that explicit constructors cannot be used. It’s guaranteed that only a single constructor is called.
(h) and (i) are also copy initialization, but simpler.
auto w = x; // (h)
auto w = widget{x}; // (i)
The semantics are just like (f) and (g), except simpler to teach, learn, and use because using auto guarantees the right-hand expression’s type will be deduced exactly. Note that the (i) syntax works fine for both implicit and explicit conversions.
Line (h) means the same as (d), type_of_x w(x);. Only a single copy constructor is called. This is guaranteed to stay true as the program evolves: Because line (h) does not commit to an explicit type, it is guaranteed to be both maximally efficient because there can be no conversion involved, and maximally robust under maintenance as the type of w “auto”-matically tracks the type of x which may change as the program is maintained.
Line (i) is the most consistent spelling when you do want to commit to a specific type and explicitly request a conversion if needed, and once again the { } syntax happily avoids lossy narrowing conversions. In practice on most compilers, only a single constructor is called—similarly to what we saw with (f) and (g), conceptually there are two constructor calls, a converting or copy constructor to create a temporary widget{x} followed by a move to move it to w, but compilers routinely elide the latter.
In general, I recommend that you try out these two forms, and increasingly prefer using them as you grow comfortable with them. I’m at the point where I’m now inclined to write virtually all of my local variable declarations this way. (I know some of you will be skeptical about this broad claim—more on “the auto question” in another GotW.)
2. What do each of the following lines do?
In the Question 2 code, we’re creating a vector<int> and passing the arguments 10 and 20 to its constructor—in the first case as ( 10, 20 ) and in the second case as { 10, 20 }.
Both will call a constructor, but which one(s)? Well, vector<int> has several constructors that can take two parameters, but only two could be correctly called with the parameters 10 and 20. Ignoring defaulted optional allocator parameters for simplicity, the two constructors are:
vector( size_t n, const int& value ); // A: n copies of value
vector( initializer_list<int> values ); // B: copy of values
There are two simple C++ rules that tell us which one will be called for the code in question:
The syntax { /*…*/ } used in an expression context gives you an initializer_list.
Constructors that take an initializer_list are preferred over other constructors, and so can hide other constructors that might otherwise be viable.
Armed with those two tidbits, the answer is simple:
vector<int> v1( 10, 20 ); // (a) calls A: 10 copies of the value 20
assert( v1.size() == 10 );
vector<int> v2{ 10, 20 }; // (b) calls B: the values 10 and 20
assert( v2.size() == 2 );
3. Besides the cases above, what other benefits are there to using { } to initialize objects?
For one thing, it’s called “uniform initialization” because it’s, well, uniform—the same for all types, including aggregate structs and arrays and std:: containers, and without the “vexing parse” annoyance:
struct mystruct { int x, y; };
// C++98
rectangle w( origin(), extents() ); // oops, vexing parse
complex<double> c( 2.71828, 3.14159 );
mystruct m = { 1, 2 };
int a[] = { 1, 2, 3, 4 };
vector<int> v; // urk, need more code
for( int i = 1; i <= 4; ++i ) v.push_back(i); // to initialize this
// C++11 (note: "=" is mostly optional)
rectangle w = { origin(), extents() };
complex<double> c = { 2.71828, 3.14159 };
mystruct m = { 1, 2 };
int a[] = { 1, 2, 3, 4 };
vector<int> v = { 1, 2, 3, 4 };
And note that this isn’t just an aesthetic issue. Consider writing generic code that should be able to initialize any type… and while we’re at it, let’s gratuitously use perfect forwarding as an example:
template<typename T, typename ...Args>
void forwarder( Args&&... args ) {
// ...
T local = { std::forward<Args>(args)... };
// ...
}
forwarder<int> ( 42 ); // ok
forwarder<rectangle> ( origin(), extents() ); // ok
forwarder<complex<double>>( 2.71828, 3.14159 ); // ok
forwarder<mystruct> ( 1, 2 ); // ok because of {}
forwarder<int[]> ( 1, 2, 3, 4 ); // ok because of {}
forwarder<vector<int>> ( 1, 2, 3, 4 ); // ok because of {}
The last three lines would not be legal if forwarder used ( ) initialization syntax internally.
The new { } syntax works pretty much everywhere, including to initialize members:
widget::widget( /*...*/ ) : mem1{init1}, mem2{init2, init3} { /*...*/ }
And, as icing on the take, it’s often just plain convenient to pass function arguments, or return a value, without a type-named temporary:
void draw_rect( rectangle );
draw_rect( rectangle(origin, selection) ); // C++98
draw_rect({ origin, selection }); // C++11
rectangle compute_rect() {
// ...
if(cpp98) return rectangle(origin, selection); // C++98
else return {origin, selection}; // C++11
}
4. When should you use ( ) vs. { } syntax to initialize objects? Why?
Here’s the simple guideline:
Guideline: Prefer to use initialization with { }, such as vector<int> v = { 1, 2, 3, 4 }; or auto v = vector<int>{ 1, 2, 3, 4 };, because it’s more consistent, more correct, and avoids having to know about old-style pitfalls at all. In single-argument cases where you prefer to see only the = sign, such as int i = 42; and auto x = anything; omitting the braces is fine. …
That covers the vast majority of cases. There is only one main exception:
… In rare cases, such as vector<int> v(10,20); or auto v = vector<int>(10,20);, use initialization with ( ) to explicitly call a constructor that is otherwise hidden by an initializer_list constructor.
However, the reason this should be generally “rare” is because default and copy construction are already special and work fine with { }, and good class design now mostly avoids the resort-to-( ) case for user-defined constructors because of this final design guideline:
Guideline: When you design a class, avoid providing a constructor that ambiguously overloads with an initializer_list constructor, so that users won’t need to use ( ) to reach such a hidden constructor.
Acknowledgments
Thanks in particular to the following for their feedback to improve this article: Michal Mocny, Jay Miller, “Alexey,” “praetorian20,” Francisco Lopes, “Neil,” Daryle Walker.
Filed under: C++, GotW
Read More »
GotW #1: Variable Initialization—or Is It? (3/10)
This first problem highlights the importance of understanding what you write. Here we have a few simple lines of code — most of which mean something different from all the others, even though the syntax varies only slightly.
Problem
JG Question
1. What is the difference, if any, among the following?
widget w; // (a)
widget w(); // (b)
widget w{}; // (c)
widget w(x); // (d)
widget w{x}; // (e)
widget w = x; // (f)
widget w = {x}; // (g)
auto w = x; // (h)
auto w = widget{x}; // (i)
Guru Questions
2. What do each of the following lines do?
vector<int> v1( 10, 20 ); // (a)
vector<int> v2{ 10, 20 }; // (b)
3. Besides the cases above, what other benefits are there to using { } to initialize objects?
4. When should you use ( ) vs. { } syntax to initialize objects? Why?
Filed under: GotW
Read More »
Guru of the Week and the Exceptional C++ Series
It's time for me to pick up Guru of the Week (GotW) again in earnest, as part of work on revising my three Exceptional C++ books for today's C++. Most Exceptional C++ Items are enhanced versions of GotW issues, after all, so the simplest and best place to start is with GotW. It's also much easier to write (and read) one short piece at a time.
Last spring I wrote a few GotWs, but life got busy with the Standard C++ Foundation, isocpp.org, and C++14. I was also trying to figure out what reasonable tools to use to write new Items, since I wanted to publish the same Item on this blog and then in e-book and dead tree editions, all with a single source while maintaining a pleasing format. I've found a provisional promising tool (Word!) and format for that I'll try out for now, and you'll see it in the next GotW issue to be posted soon.
But a major issue was figuring out the right balance between updating existing GotW issues and writing new ones.
Here's where I landed…
First, I've decided to keep and revise nearly all of my existing Items. This is not an obvious choice: As a counterexample, some authors have decided to not revise their existing books, but to write new books about C++11, for various reasons including that they expect the advice in the earlier books is still current. I think the latter is partly true, because of course the topics are still current and the code still compiles – C++11 has a great backward compatibility story with C++98.
However, when I looked through my own articles and Items, every single one I looked at had two qualities:
The topic was still current and useful, so it should still be covered. (With very rare exceptions like material discussing the now-removed export template feature.)
The code and discussion were addressed with C++98-era advice that is now dated and/or incomplete.
For example, nearly every GotW's code should just naturally use auto and { } initialization. Most should use or actively discuss move semantics, lambdas, and other C++11 features. And it's hard to write about existing features like virtual function overriding (as in GotW #5) without feeling something important is missing unless there's also discussion of the way C++ has extended the existing feature, such as with override and final.
Second, I've decided not to target C++11 – rather, I'm going to write for C++14. Why would I do that, when C++ compilers [preemptive snarky remark: including my own team's] are still catching up to C++11? For several good reasons:
C++14 is going to be current C++ soon. As of April, it's now feature-complete, and the detailed technical specification should be frozen this fall or winter (leaving time for ISO ballots and "'14" publication). Compilers and libraries are already aggressively implementing it. I wouldn't be surprised if we had multiple fully-conforming C++14 implementations by the end of next year.
C++14 is a small delta, a minor release aimed mainly at "completing C++11." And it does complete C++11 well, to the point where as an author I really want to teach the complete story. I want to teach the complete "avoid writing new and delete" guidance that C++14 make_unique fully enables so that I can stop mumbling "except when you create a unique_ptr" as a special case. I want to write generic lambdas with C++14's terse auto parameters that makes lambdas shorter to write and more flexible, and stop repeating parameter type names. I want to use C++14 move-capture in lambdas that makes them complete, and not feel obliged to talk about artificially putting objects on the heap and capturing shared_ptrs to them by value as a workaround for lack of move capture. And, in a few places where appropriate, I want to be free to use a few smaller C++14 tidbits like optional<T> that can affect interface design guidance now that they're in the draft standard.
In addition, I've found that C++11/14 is changing my coding style, including my basic variable declaration style. I now think auto should be used consistently to declare local variables, even when you want to commit to a specific type(!) – this certainly surprised me, and likely will surprise you too, so I'll cover that this year in a GotW and a recorded talk, no later than at C++ and Beyond later this year and possibly sooner.
I've converged on the above by updating the first dozen Exceptional C++ Items (in GotW order), trying out alternatives and seeing how well each one worked out. Those first dozen Items are now done and I'm pleased with the result, showing off auto and move and generic lambdas and mutexes/atomics and the new meanings of const and mutable and everything else, most of their code tested against various of the latest compilers, their advice updated to be current with current C++. And in every single GotW, no matter how short, it just felt right to:
keep the topic, because each topic is still as relevant and useful as ever;
revise the C++98 code and advice, because in every case both the code examples and the discussion was dated and/or incomplete, usually both; and
skip C++11 to write directly for the complete story in C++14 without apology, because it let me avoid digressions for workarounds not needed in C++14, which made the text cleaner and simpler.
As I've revised Items, I've found that some updates are small but pervasive, such as using auto. Others are extensive, with entire subsections completely rewritten, thrown out as no longer relevant, or newly added to cover essential new sides of the story in modern C++. For example, GotW #6 about const-correctness is now a two-parter, covering the new meanings of const and mutable. Watch for it soon.
A word about numbering and sequence: My plan is to update and post most GotWs in numerical order starting with GotW #1, but every so often we'll jump forward to new high numbers (starting with #89) as I create ones that weren't originally a GotW – a Sutter's Mill magazine article that wasn't originally published as a GotW, or a brand-new C++14 topic. So for example you might see a sequence like "… 23, 24, 91, 25, 26, …". All GotWs will have substantial new material, but the newly minted high numbers will be entirely on brand-new topics that didn't arise in C++98 or just weren't covered in GotW form before. (The handful of GotWs I wrote last year will be renumbered as they're folded into the original series, either as updates or with new numbers in the original sequence if they're on completely new topics. )
I'll start posting fresh C++11/14 GotWs in the next few days. As usual, I'll first post a problem by itself to invite discussion about possible solutions, then after there's been time for comments I'll post my own solution and the next problem.
It's kind of exciting to write "GotW14" – today's C++ really does feel like a fresh new language.
I hope you enjoy them as much as I enjoy writing them.
Filed under: GotW
Read More »
Trip Report: ISO C++ Spring 2013 Meeting
The Bristol meeting concluded a few hours ago, and I just posted my trip report on isocpp.org:
This afternoon in Bristol, UK, the ISO C++ standards committee adopted generic lambdas, dynamic arrays (an improved version of C99 VLAs), variable templates, reader/writer locks, make_unique, optional<T>, standard library user-defined literals, and a number of other language and library improvements – and approved the result as the feature-complete Committee Draft (CD) of Standard C++14 to be distributed for its primary international review ballot.
In addition to completing the C++14 CD document, the committee also made progress on three additional important parallel specifications that are on track to be published around the same time as C++14:
File system library (draft), based on Boost.FileSystem version 3.
Networking library, small at first and regularly extended.
"Concepts Lite" language extensions (draft), to express template constraints and improve template usability and error messages.
Together these mark the C++ committee's main planned deliverables for 2014. …
To provide just a sampling [of C++14], here are a few quick examples of some of the newly added features…
Continue reading…
Filed under: C++
Read More »
Complex initialization for a const variable
On std-discussion, Shakti Misra asked:
I have seen in a lot of places code like
int i;
if(someConditionIstrue)
{
Do some operations and calculate the value of i;
i = some calculated value;
}
use i; //Note this value is only used not changed. It should not be changed.
But unfortunately in this case there is no way to guarantee it.
so now if some one comes and does
i = 10;// This is valid
What i was thinking: is there a way to tell that "i" can be set only once?
After that it will be a constant. Something like
const once int i;
if(someConditionIstrue)
{
Do some operations and calculate the value of i;
i = calculated value;
}
use i; //Note this value is only used not changed.
i = 10;// Compiler error
Olaf van der Spek replied:
A lambda?
I answered as follows:
Bingo. Olaf nailed it: The way to do it is with a lambda. This is one of the examples I give in my talk Lambdas, Lambdas Everywhere (here are the slides from C++ & Beyond 2010).
In your example, do this:
const int i = [&]{
int i = some_default_value;
if(someConditionIstrue)
{
Do some operations and calculate the value of i;
i = some calculated value;
}
return i;
} (); // note: () invokes the lambda!
That’s the C++11 idiom for this. It’s still being absorbed though – not everyone knows about it yet as we’re still all learning C++11 styles together.
Filed under: C++
Read More »
Words of wisdom: Bjarne Stroustrup
Bjarne Stroustrup wrote the following a few minutes ago on the concepts mailing list:
Let me take this opportunity to remind people that
"being able to do something is not sufficient reason for doing it" and
"being able to do every trick is not a feature but a bug"
For the latter, remember Dijkstra’s famous "Goto considered harmful" paper. The point was not that the "new features" (loop constructs) could do every goto trick better/simpler, but that some of those tricks should be avoided to simplify good programming.
Concepts and concepts lite are meant to make good generic programming simpler. They are not meant to be a drop-in substitute for every metaprogramming and macroprogramming trick. If you are an expert, and if in your expert opinion you and your users really need those tricks, you can still use them, but we need to make many (most) uses of templates easier to get right, so that they can become more mainstream. That where concepts and concept lite fits in.
Some of you may find this hard to believe, but "back then" there was quite serious opposition to function declarations because "they restricted the way functions could be used and the way separate compilation could be used" and also serious opposition to virtual functions "because pointers to functions are so much more flexible." I see concepts lite (and concepts) in the same light as goto/for, unchecked-function-arguments/function-declarations, pointers-to-functions/abstract-classes.
Reminds me of the related antipattern: "Something must be done. This is something. Therefore we must do it!"
Filed under: C++, Opinion & Editorial, Software Development
Read More »
atomic Weapons: The C++ Memory Model and Modern Hardware
Most of the talks I gave at C++ and Beyond 2012 last summer are already online at Channel 9. Here are two more.
This is a two-part talk that covers the C++ memory model, how locks and atomics and fences interact and map to hardware, and more. Even though we’re talking about C++, much of this is also applicable to Java and .NET which have similar memory models, but not all the features of C++ (such as relaxed atomics).
Note: This is about the basic structure and tools, not how to write lock-free algorithms using atomics. That next-level topic may be on deck for this year’s C++ and Beyond in December, we’ll see…
atomic<> Weapons: The C++ Memory Model and Modern Hardware
Part 1: Optimizations, races, and the memory model; acquire and release ordering; mutexes vs. atomics vs. fences
Part 2: Restrictions on compilers and hardware (incl. common bugs); code generation and performance on x86/x64, IA64, POWER, ARM, and more; relaxed atomics; volatile
This session in one word: Deep.
It’s a session that includes topics I’ve publicly said for years is Stuff You Shouldn’t Need To Know and I Just Won’t Teach, but it’s becoming achingly clear that people do need to know about it. Achingly, heartbreakingly clear, because some hardware incents you to pull out the big guns to achieve top performance, and C++ programmers just are so addicted to full performance that they’ll reach for the big red levers with the flashing warning lights. Since we can’t keep people from pulling the big red levers, we’d better document the A to Z of what the levers actually do, so that people don’t SCRAM unless they really, really, really meant to.
Topics Covered:
The facts: The C++11 memory model and what it requires you to do to make sure your code is correct and stays correct. We’ll include clear answers to several FAQs: “how do the compiler and hardware cooperate to remember how to respect these rules?”, “what is a race condition?”, and the ageless one-hand-clapping question “how is a race condition like a debugger?”
The tools: The deep interrelationships and fundamental tradeoffs among mutexes, atomics, and fences/barriers. I’ll try to convince you why standalone memory barriers are bad, and why barriers should always be associated with a specific load or store.
The unspeakables: I’ll grudgingly and reluctantly talk about the Thing I Said I’d Never Teach That Programmers Should Never Need To Now: relaxed atomics. Don’t use them! If you can avoid it. But here’s what you need to know, even though it would be nice if you didn’t need to know it.
The rapidly-changing hardware reality: How locks and atomics map to hardware instructions on ARM and x86/x64, and throw in POWER and Itanium for good measure – and I’ll cover how and why the answers are actually different last year and this year, and how they will likely be different again a few years from now. We’ll cover how the latest CPU and GPU hardware memory models are rapidly evolving, and how this directly affects C++ programmers.
Filed under: C++, Concurrency, Talks & Events
Read More »
Videos: Panel, and C++ Concurrency
I'm about two weeks late posting this, but two more C++ and Beyond 2012 videos are now available online.
The first is my concurrency talk:
C++ and Beyond 2012: C++ Concurrency (Herb Sutter)
I've spoken and written on these topics before. Here's what's different about this talk:
Brand new: This material goes beyond what I've written and taught about before in my Effective Concurrency articles and courses.
Cutting-edge current: It covers the best-practices state of the art techniques and shipping tools, and what parts of that are standardized in C++11 already (the answer to that one may surprise you!) and what's en route to near-term standardization and why, with coverage of the latest discussions.
Blocking vs. non-blocking: What's the difference between blocking and non-blocking styles, why on earth would you care, which kinds does C++11 support, and how are we looking at rounding it out in C++1y?
The answers all matter to you – even the ones not yet in the C++ standard – because they are real, available in shipping products, and affect how you design your software today.
The second is one of the panels:
C++ and Beyond 2012: Panel – Convincing your Colleagues
From C++ and Beyond 2012, Andrei, Herb and Scott present Convincing Your Colleagues – an interactive panel.
Abstract:
You can't do a better job if you don't change what you're doing, but change is hard. It's especially hard when what needs to change is your colleagues' approach to software development. Moving your team forward often requires persuading your peers to change their behavior, sometimes to do something they're not doing, other times to stop doing something they've become accustomed to. Whether the issue is to embrace or avoid C++ language features, to adopt new development tools or abandon old ones, to increase use of or scale back on overuse of design patterns, to adhere to coding standards, or any of the plethora of other matters that affect software creation, moving things forward typically requires getting your colleagues to buy into the change you're proposing. But how can you do that?
In this panel session, Andrei, Herb, and Scott share how they go about convincing their colleagues to change and take questions from the audience.
Truth be told, the panel ranged widely and probably most of the time was on other topics!
I hope you find them useful.
Filed under: C++, Talks & Events
Read More »
Java vulnerabilities
With the help of friends Robert Seacord and David Svoboda of CERT in particular, I posted a note and link to their CERT post today because people have been misunderstanding the recent Java vulnerabilities, thinking they're somehow really C or C++ vulnerabilities because Java is implemented in C and C++.
From the post:
Are the Java vulnerabilities actually C and C++ vulnerabilities?
by Herb Sutter
You've probably seen the headlines:
[US-CERT] Java in Web Browser: Disable Now!
We've been telling people to disable Java for years. … We have confirmed that VU#625617 can be used to reliably execute code on Windows, OS X, and Linux platforms. And the exploit code for the vulnerability is publicly available and already incorporated into exploit kits. This should be enough motivation for you to turn Java off.
Firefox and Apple have blocked Java while U.S. Homeland Security recommends everyone disable it, because of vulnerabilities
Homeland Security still advises disabling Java, even after update
Some people have asked whether last week's and similar recent Java vulnerabilities are actually C or C++ vulnerabilities – because, like virtually all modern systems software, Java is implemented in C and C++.
The answer is no, these particular exploits are pure Java. Some other exploits have indeed used vulnerabilities in Java's native C code implementation, but the major vulnerabilities in the news lately are in Java itself, and they enable portable exploits on any operating system with a single program. …
Some other C++ experts who have better sense than I do won't add the following bit publicly, but I can't help myself: Insert "write once, pwn everywhere" joke here…
Filed under: C++, Java, Software Development
Read More »
Video: You Don't Know const and mutable
At C++ and Beyond in August, I gave a 30 min talk on the changed meaning of const and mutable. The talk video is now online:
You Don't Know [keyword] and [keyword]
const means const.
Bonus: mutable is useful and continues to mean 'already as good as const.'
This is another way C++ has become simpler: const now means what people always thought it meant, and the four-line code example doesn't need deep analysis at all – it just works.
But we analyze the four-line example anyway as a motivating case to see why and how it works, so we can fully appreciate this new simplification in C++11…
Filed under: C++
Read More »
An implementation of generic lambdas is now available
For those interested in C++ standardization and not already following along at isocpp.org, here’s an item of likely interest:
An implementation of generic lambdas (request for feedback)—Faisal Vali
This week, Faisal Vali shared an initial “alpha” implementation of generic lambdas in Clang. Faisal is the lead author of the proposal (N3418), with Herb Sutter and Dave Abrahams.
To read and participate in the active discussion, see the message thread on std-proposals.
Here is a copy of Faisal’s announcement…
Read more at isocpp.org…
Filed under: C++
Read More »
Compatibility
On yesterday's thread, I just wrote in a comment:
@Jon: Yes, C++ is complex and the complexity is largely because of C compatibility. I agree with Bjarne that there's a small language struggling to get out — I've participated in private experiments to specify such a language, and you can do it in well under half the complexity of C++ without losing the expressivity and flexibility of C++. However, it does require changes to syntax (mainly) and semantics (secondarily) that amount to making it a new language, which makes it a massive breaking change for programs (where existing code doesn't compile and would need to be rewritten manually or with a tool) and programmers (where existing developers need retraining). That's a very big tradeoff. So just as C++ 'won' in the 90s because of its own strengths plus its C compatibility, C++11 is being adopted because of its own strengths plus its C++98 compatibility. At the end of the day compatibility continues to be a very strong force and advantage that isn't lightly tossed aside to "improve" things. However, it's still worthwhile and some interesting things may come of it. Stay tuned, but expect (possible) news in years rather than months.
That reminded me of a snippet from the September 2012 issues of CACM. When I first read it, I thought it was so worthwhile that I made a magnified copy and pasted it on the window outside my office at work – it's still there. The lightly annotated conclusion is shown here.
The article itself was about a different technology, but the Lessons Learned conclusion remind us of the paramount importance of two things:
Backward compatibility with simple migration path ("compat"). That's what keeps you "in the ecosystem." If you don't have such compatibility, don't expect wide adoption unless there are hugely compelling reasons to put up with the breaking change. It's roughly the same kind of ecosystem change for programmers to go from Language X to Language Y as for users to go from Platform X to Platform Y (e.g., Windows to Mac, iPhone to Android) – in each case you have a lot of relearning, and you lose understood tools and services some of which have replacements and some of which don't.
Focusing on complete end-to-end solutions ("SFE" or scenario-focused engineering). This is why it's important not to ship just a bunch of individual features, because that can leave holes where Steps 1, 2, and 4 of an end-to-end experience are wonderful but Step 3 is awkward, unreliable, or incomplete, which renders much of the good work in the other steps unuseful since they can't be used as much or possibly at all.
Enjoy.
Filed under: C++, Software Development
Read More »
Perspective: "Why C++ Is Not 'Back'"
John Sonmez wrote a nice article on the weekend – both the article and the comments are worth reading.
"Why C++ Is Not 'Back'"
by John Sonmez
I love C++. […] There are plenty of excellent developers I know today that still use C++ and teach others how to use it and there is nothing at all wrong with that.
So what is the problem then?
[…] Everyone keeps asking me if they need to learn C++, but just like my answer was a few years ago, it is the same today—NO!
Ok, so "NO" in caps is a bit harsh. A better answer is "why?"
[…]
Although I don't agree with everything John says, he presents something quite valuable, and unfortunately rare: a thoughtful hype-free opinion. This is valuable especially when (not just "even when") it differs from your own opinion, because combining different thoughtful views of the same thing gives something exceedingly important and precious: perspective.
By definition, depth perception is something you get from seeing and combining more than one point of view. This is why one of my favorite parts of any conference or group interview is discussions between experts on points where they disagree – because experts do regularly disagree, and when they each present their thoughtful reasons and qualifications and specific cases (not just hype), you get to see why a point is valid, when it is valid and not valid, how it applies to a specific situation or doesn't, and so on.
I encourage you to read the article and the comments. This quote isn't the only thing I don't fully agree with, but it's the one thing I'll respond to a little from the article:
There are only about three sensible reasons to learn C++ today that I can think of.
There are other major reasons in addition to those, such as:
Servicing, which is harder when you depend on a runtime.
Testing, since you lose the ability to test your entire application (compare doing all-static or mostly-static linking with having your application often be compiled/jitted for the first time on an end user's machine).
These aren't bad things in themselves, just tradeoffs and reasons to prefer native code vs managed code depending on your application's needs.
But even the three points John does mention are very broad reasons that apply often, especially the issue of control over memory layouts, to the point where I would say: In any language, if you are serious about performance you will be using arrays a lot (not "always," just "a lot"). Some languages make that easier and give you much better control over layout in general and arrays in particular, while other languages/environments make it harder (possible! but harder) and you have to "opt out" or “work against” the language's/runtime's strong preference for pointer-chasing node-based data structures.
For more, please see also my April 2012 Lang.NEXT talk "(Not Your Father's) C++", especially the second half:
From 19:20, I try to contrast the value and tenets of C++ and of managed language/environments, including that each is making a legitimate and useful design choice.
From 36:00, I try to address the question of: "can managed languages do essentially everything that's interesting, so native C/C++ code should be really just for things like device drivers?"
From 39:00, I talk about when each one is valuable and should be used, especially that if programmer time is your biggest cost, that's what you should optimize for and it's exactly what managed languages/environments are designed for.
But again, I encourage you to read John's article – and the comments – yourself. There's an unusually high signal-to-noise ratio here.
It's always nice to encounter a thoughtful balanced piece of writing, whether or not we agree with every detail. Thanks, John!
Filed under: C# / .NET, C++
Read More »
"256 cores by 2013"?
I just saw a tweet that's worth commenting on:
Almost right, and we have already reached that.
I said something similar to the above, but with two important differences:
I said hardware "threads," not only hardware "cores" – it was about the amount of hardware parallelism available on a mainstream system.
What I gave was a min/max range estimate of roughly 16 to 256 (the latter being threads) under different sets of assumptions.
So: Was I was right about 2013 estimates?
Yes, pretty much, and in fact we already reached or exceeded that in 2011 and 2012:
Lower estimate line: In 2011 and 2012 parts, Intel Core i7 Sandy Bridge and Ivy Bridge are delivering almost the expected lower baseline, and offering 8-way and 12-way parallelism = 4-6 cores x 2 hardware threads per core.
Upper estimate line: In 2012, as mentioned in the article (which called it Larrabee, now known as MIC or Xeon Phi) is delivering 200-way to 256-way parallelism = 50-64 cores x 4 hardware threads per core. Also, in 2011 and 2012, GPUs have since emerged into more mainstream use for computation (GPGPU), and likewise offer massive compute parallelism, such as 1,536-way parallelism on a machine having a single NVidia Tesla card.
Yes, mainstream machines do in fact have examples of both ends of the "16 to 256 way parallelism" range. And beyond the upper end of the range, in fact, for those with higher-end graphics cards.
For more on these various kinds of compute cores and threads, see also my article Welcome to the Jungle.
Longer answer follows:
Here's the main part from article, "Design for Manycore Systems" (August 11, 2009). Remember this was written over three years ago – in the Time Before iPad, when Android was under a year old:
How Much Scalability Does Your Application Need?
So how much parallel scalability should you aim to support in the application you're working on today, assuming that it’s compute-bound already or you can add killer features that are compute-bound and also amenable to parallel execution? The answer is that you want to match your application’s scalability to the amount of hardware parallelism in the target hardware that will be available during your application’s expected production or shelf lifetime. As shown in Figure 4, that equates to the number of hardware threads you expect to have on your end users’ machines.
Figure 4: How much concurrency does your program need in order to exploit given hardware?
Let’s say that YourCurrentApplication 1.0 will ship next year (mid-2010), and you expect that it’ll be another 18 months until you ship the 2.0 release (early 2012) and probably another 18 months after that before most users will have upgraded (mid-2013). Then you’d be interested in judging what will be the likely mainstream hardware target up to mid-2013.
If we stick with "just more of the same" as in Figure 2′s extrapolation, we’d expect aggressive early hardware adopters to be running 16-core machines (possibly double that if they’re aggressive enough to run dual-CPU workstations with two sockets), and we’d likely expect most general mainstream users to have 4-, 8- or maybe a smattering of 16-core machines (accounting for the time for new chips to be adopted in the marketplace). [[Note: I often get lazy and say "core" to mean all hardware parallelism. In context above and below, it's clear we're talking about "cores and threads."]]
But what if the gating factor, parallel-ready software, goes away? Then CPU vendors would be free to take advantage of options like the one-time 16-fold hardware parallelism jump illustrated in Figure 3, and we get an envelope like that shown in Figure 5.
Figure 5: Extrapolation of "more of the same big cores" and "possible one-time switch to 4x smaller cores plus 4x threads per core" (not counting some transistors being used for other things like on-chip GPUs).
First, let's look at the lower baseline, 'most general mainstream users to have [4-16 way parallelism] machines in 2013'? So where are were in 2012 today for mainstream CPU hardware parallelism? Well, Intel Core i7 (e.g., Sandy Bridge, Ivy Bridge) are typically in the 4 to 6 core range – which, with hyperthreading == hardware threads, means 8 to 12 hardware threads.
Second, what about the higher potential line for 2013? As noted above:
Intel's Xeon Phi (then Larrabee) is now delivering 50-64 cores x 4 threads = 200 to 256-way parallelism. That's no surprise, because this article's upper line was based on exactly the Larrabee data point (see quote below).
GPUs already blow the 256 upper bound away – any machine with a two-year-old Tesla has 1,536-way parallelism for programs (including mainstream programs like DVD encoders) that can harness the GPU.
So not only did we already reach the 2013 upper line early, in 2012, but we already exceeded it for applications that can harness the GPU for computation.
As I said in the article:
I don’t believe either the bottom line or the top line is the exact truth, but as long as sufficient parallel-capable software comes along, the truth will probably be somewhere in between, especially if we have processors that offer a mix of large- and small-core chips, or that use some chip real estate to bring GPUs or other devices on-die. That’s more hardware parallelism, and sooner, than most mainstream developers I’ve encountered expect.
Interestingly, though, we already noted two current examples: Sun’s Niagara, and Intel’s Larrabee, already provide double-digit parallelism in mainstream hardware via smaller cores with four or eight hardware threads each. "Manycore" chips, or perhaps more correctly "manythread" chips, are just waiting to enter the mainstream. Intel could have built a nice 100-core part in 2006. The gating factor is the software that can exploit the hardware parallelism; that is, the gating factor is you and me.
Filed under: Concurrency, Software Development
Read More »
Podcast: Interview on Hanselminutes
A few weeks ago at the Build conference, Scott Hanselman and I sat down to talk about C++ and modern UI/UX. The podcast is now live here:
The Hanselminutes Podcast, Show #346
"Why C++" with Herb Sutter
Topics Scott raises include:
2:00 Scott mentions he has used C++ in the past. C++ has changed. We still call it C++, but it's a very different language now.
5:30 (Why) do we care about performance any more?
10:00 What's this GPGPU thing? Think of your GPU as your modern 80387.
13:45 C++ is having a resurgence. Where is C++ big?
18:00 Why not just use one language? or, What is C++ good at? Efficient abstraction and portability.
21:45 Programmers have a responsibility to support the business. Avoid the pitfall of speeds & feeds.
24:00 My experience with my iPad, my iPhone, and my Slate 7 with Win8.
28:45 We're in two election seasons – (a) political and (b) technology (Nexus, iPad Mini, Surface, …). Everyone is wallpapering the media with ads (some of them attack ads), and vying for customer votes/$$, and seeing who's going to be the winner.
35:00 Natural user interfaces – we get so easily used to touch that we paw all screen, and Scott's son gets so used to saying "Xbox pause" that anything that doesn't respond is "broken."
Filed under: C++, Software Development
Read More »
Reader Q&A: A good book to learn C++11?
Last night a reader asked one of the questions that helped motivate the creation of isocpp.org:
I am trying to learn the new C++. I am wondering if you are aware of resources or courses that can help me learn a little. I was not able to find any books for C++11. Any help would be greatly appreciated.
By the way, the isocpp website is great :)
Thanks! And you beat me to the punchline. :)
A good place to start is isocpp.org/get-started. It recommends four books, three of which have been updated for C++11 (and two of those three are available now). C++ Primer 5e is a good choice.
We also maintain a list of new articles and books under isocpp.org/blog/category/articles-books.
As for courses, there's also a growing list of good courses there under isocpp.org/blog/category/training, including two C++11 overview courses by Scott Meyers and Dave Abrahams respectively.
As with all blog categories, you have three ways to find the most recent items under Articles & Books, Training, and other categories:
Home page: The most recent items in each category are always listed right on the site's newspaper-inspired home page.
RSS: You can subscribe to RSS feeds for all posts, or for specific categories to get notifications of new articles, books, training, events, and more as they become available.
Twitter: Follow @isocpp for notifications of new stuff.
The site and blog are curated, aiming for higher quality and lower volume. Even so, we know you won't be able to read, watch, or listen to all of the content. So we're doing our best to make sure that when you do pick something recommended by the site, it's likely to be of high quality and address what you're looking to find.
Enjoy! This is version 1 of the site… I hope that it's already useful, and you'll see quite a bit more over the coming year.
Filed under: C++
Read More »
Friday's Q&A session now online
My live Q&A after Friday's The Future of C++ talk is now online on Channel 9. The topics revolved around…
… recent progress and near-future directions for C++, both at Microsoft and across the industry, and talks about some announcements related to C++11 support in VC++ 2012 and the formation of the Standard C++ Foundation.
Herb takes questions from a live virtual audience and demos the new http://isocpp.org site on an 82 inch Perceptive Pixel display attached to a Windows 8 machine.
Thanks to everyone who tuned in.
Filed under: C++, Microsoft, Software Development
Read More »
Our industry is young again, and it's all about UI
Jeff Atwood's post two days ago inspired me to write this down. Thanks, Jeff.
"I can’t even remember the last time I was this excited about a computer."
– Jeff Atwood, November 1, 2012
Our industry is young again, full of the bliss and sense of wonder and promise of adventure that comes with youth.
Computing feels young and fresh in a way that it hasn't felt for years, and that has only happened to this degree at two other times in its history. Many old-timers, including myself, have said "this feels like 1980 again."
It does indeed. And the reason why is all about user interfaces (UI).
Wave 1: Late 1950s through 60s
First, computing felt young in the late 1950s through the 60s because it was young, and made computers personally available to a select few people. Having computers at all was new, and the ability to make a machine do things opened up a whole new world for a band of pioneers like Dijkstra and Hoare, and Russell (Spacewar!) and Engelbart (Mother of All Demos) who made these computers personal for at least a few people.
The machines were useful. But the excitement came from personally interacting with the machine.
Wave 2: Late 1970s through 80s
Second, computing felt young again in the late 1970s and 80s. Then, truly personal single-user computers were new. They opened up to a far wider audience the sense of wonder that came with having a computer of our very own, and often even with a colorful graphical interface to draw us into its new worlds. I'll include Woods and Crowther (ADVENT) as an example, because they used a PDP as a personal computer (smile) and their game and many more like it took off on the earliest true PCs – Exidy Sorcerers and TRS-80s, Ataris and Apples. This was the second and much bigger wave of delivering computers we could personally interact with.
The machines were somewhat useful; people kept trying to justify paying $1,000 for one "to organize recipes." (Really.) But the real reason people wanted them was that they were more intimate – the excitement once again came from personally interacting with the machine.
Non-wave: 1990s through mid-2000s
Although WIMP interfaces proliferated in the 1990s and did deliver benefits and usability, they were never as exciting to the degree computers were in the 80s. Why not? Because they weren't nearly as transformative in making computers more personal, more fun. And then, to add insult to injury, once we shipped WIMPiness throughout the industry, we called it good for a decade and innovation in user interfaces stagnated.
I heard many people wonder whether computing was done, whether this was all there would be. Thanks, Apple, for once again taking the lead in proving them wrong.
Wave 3: Late 2000s through the 10s
Now, starting in the late 2000s and through the 10s, modern mobile computers are new and more personal than ever, and they're just getting started. But what makes them so much more personal? There are three components of the new age of computing, and they're all about UI (user interfaces)… count 'em:
Touch.
Speech.
Gestures.
Now don't get me wrong, these are in addition to keyboards and accurate pointing (mice, trackpads) and writing (pens), not instead of them. I don't believe for a minute that keyboards and mice and pens are going away, because they're incredibly useful – I agree with Joey Hess (HT to @codinghorror):
"If it doesn't have a keyboard, I feel that my thoughts are being forced out through a straw."
Nevertheless, touch, speech, and gestures are clearly important. Why? Because interacting with touch and speech and gestures is how we're made, and that's what lets these interactions power a new wave of making computers more personal. All three are coming to the mainstream in about that order…
Four predictions
… and all three aren't done, they're just getting started, and we can now see that at least the first two are inevitable. Consider:
Touchable screens on smartphones and tablets is just the beginning. Once we taste the ability to touch any screen, we immediately want and expect all screens to respond to touch. One year from now, when more people have had a taste of it, no one will question whether notebooks and monitors should respond to touch – though maybe a few will still question touch televisions. Two years from now, we'll just assume that every screen should be touchable, and soon we'll forget it was ever any other way. Anyone set on building non-touch mainstream screens of any size is on the wrong side of history.
Speech recognition on phones and in the living room is just the beginning. This week I recorded a podcast with Scott Hanselman which will air in another week or two, when Scott shared something he observed firsthand in his son: Once a child experiences saying "Xbox Pause," he will expect all entertainment devices to respond to speech commands, and if they don't they're "broken." Two years from now, speech will probably be the norm as one way to deliver primary commands. (Insert Scotty joke here.)
Likewise, gestures to control entertainment and games in the living room is just the beginning. Over the past year or two, when giving talks I've sometimes enjoyed messing with audiences by "changing" a PowerPoint slide by gesturing in the air in front of the screen while really changing the slide with the remote in my pocket. I immediately share the joke, of course, and we all have a laugh together, but the audience members more and more often just think it's a new product and expect it to work. Gestures aren't just for John Anderton any more.
Bringing touch and speech and gestures to all devices is a thrilling experience. They are just the beginning of the new wave that's still growing. And this is the most personal wave so far.
This is an exciting and wonderful time to be part of our industry.
Computing is being reborn, again; we are young again.
Filed under: Friday Thoughts, Opinion & Editorial
Read More »
Talk now online: The Future of C++ (VC++, ISO C++)
Yesterday, many thousands of you were in the room or live online for my talk on The Future of C++. The talk is now available online.
This has been a phenomenal year for C++, since C++11's publication just 12 months ago. And yesterday was a great day for C++.
Yesterday I had the privilege of announcing much of what Microsoft and the industry have been working on over the past year.
(minor) C++ at Microsoft
On September 12, we shipped VC++ 2012 with the complete C++11 standard library, and adding support for C++11 range-for, enum class, override and final. Less than two months later, yesterday we announced and shipped the November 2012 CTP, a compiler add-in to VC++ 2012 adding C++11 variadic templates, uniform initialization and initializer_lists, delegating constructors, function template default arguments, explicit conversion operators, and raw string literals. Details here, and download here.
Note that this is just the first batch of additional C++11 features. Expect further announcements and deliveries in the first half of 2013.
(major) C++ across the industry
Interest and investment in C++ continues to accelerate across the software world.
ISO C++ standardization is accelerating. Major companies are dedicating more people and resources to C++ standardization than they have in years. Over the next 24 months, we plan to ship three Technical Specifications and a new C++ International Standard.
C++ now has a home on the web at isocpp.org. Launched yesterday, it both aggregates the best C++ content and hosts new content itself, including Bjarne Stroustrup's new Tour of C++ and Scott Meyers' new Universal References article.
We now have a Standard C++ Foundation. Announced yesterday, it is already funded by the largest companies in the industry down to startups, financial institutions to universities, book publishers to other consortia, with more members joining weekly. For the first time in C++'s history since AT&T relinquished control of the language, we have an entity – a trade organization – that exists exclusively to promote Standard C++ on all compilers and platforms, and companies are funding it because the world runs on C++, and investing in Standard C++ is good business.
This is an exciting time to be part of our industry, on any OS and using any language. It's especially an exciting time to be involved with C++ on all compilers and platforms.
Thank you all, whatever platform and language you use, for being part of it.
Links:
VC++ November 2012 CTP announcement
Broad C++ news release covering the CTP, standardization, site, and Foundation
My talk yesterday covering these announcements
Filed under: C++, Microsoft, Software Development, Talks & Events
Read More »
90 seconds @Build: "It's a great week for C++"
A few hours ago I sat down to give a short teaser for my webcast talk this Friday.
Here it is. Feel free to forward.
(I don't think they believed me when I said I could keep it to under two minutes.)
Filed under: C++, Microsoft, Software Development, Talks & Events
Read More »
The Future of C++: Live broadcast this Friday
In my talk on Friday, there will be announcements of broad interest to C++ developers on all compilers and platforms. Please help spread the word.
The Future of C++
Friday, November 2, 2012
12:45pm (U.S. Pacific Time)
This talk will give an update on recent progress and near-future directions for C++, both at Microsoft and across the industry, with some announcements of interest in both areas. The speaker is the lead language architect of Visual C++ and chair of the ISO C++ committee.
The talk will be webcast live on Channel 9, and available soon afterwards on demand.
If you know people who are interested in C++, on any platform, you'll want to let them know to tune in.
Filed under: C++, Microsoft, Software Development, Talks & Events
Read More »
Reader Q&A: volatile (again)
Sarmad Asgher asked a variant of a perennial question:
I am implementing multi producer single consumer problem. I have shared variables like m_currentRecordsetSize which tells the current size of the buffer. I am using m_currentRecordsetSize in a critical section do i need to declare it as volatile.
If you're in C or C++, and the variable is not already being protected by a mutex or similar, then you need to declare it atomic (e.g., if it's an int, then atomic_int in C or atomic<int> in C++. Not volatile.
Also is there any article by you on this topic. Please do reply.
There is! See my article "volatile vs. volatile" for the difference and why C/C++ volatile has nothing to do with inter-thread communication.
Filed under: C++, Effective Concurrency, Reader Q&A
Read More »
CTP of Windows XP Targeting with C++ in Visual Studio 2012
The three by-far-most-requested "missing features" from Visual C++ 2012 were:
Conformance: Keep adding more C++11 language conformance features.
XP Targeting: Deliver the ability to build applications that could run on Windows XP, as well as Windows Vista, 7, and 8.
Desktop Express: Deliver a free VC++ Express compiler that can be used to create traditional Windows desktop apps, not just Windows Store apps.
Over the spring and summer, we promised to address these "soon after VC++ 2012 ships."
Well, VC++ 2012 shipped four weeks ago.
What's happened since then?
3. On the same day VS2012 shipped, four weeks ago, we also announced and released Visual Studio 2012 Desktop Express – a free Visual Studio version for writing traditional Windows applications.
2. Today, we are pleased to share a "community tech preview" (CTP) of Windows XP targeting in Visual C++ 2012, being delivered as part of Visual Studio 2012 Update 1 CTP 3. You can download the preview here. See the announcement page for details, known issues, and full release information.
1. Stay tuned…
Filed under: C++, Microsoft
Read More »
Poll: What features would you like to see added soonest in your favorite C++ compiler?
I just got back from teaching a class, and I’m always amazed at the breadth and diversity of C++ developers. As Bjarne Stroustrup famously says: “No one knows ‘what most C++ developers do.’”
In particular, I’m surprised at how strongly some people feel about certain features, such as refactoring or safety or raw performance or exhaustive conformance, that don’t matter much at all to other people, and it made me wonder how a cross-section of the C++ developer community might prioritize them if I ran a poll.
So let me pose a question: Whether you use gcc or Clang or VC++ or Intel C++ or any other compiler, if you could only pick five of the following for your compiler to implement in its very next release, which would they be? Note that this is intended to be purely an “urgency” or ordering exercise, not an “importance” or eventually-will/won’t-do exercise — assume that your compiler vendor would eventually add everything possible from the list in their next few releases, and ask yourself only what would you just love to have in your hands the very soonest, right away?
(This is not intended to be a complete list, by the way, just picking some things I’ve again recently heard people mention, to validate how broadly interesting they really are.)
Thanks in advance for your participation. I’m really curious to see the results.
[Updated to add: Several people have asked to change the poll to add an option for "faster build times". I won't change the poll while in progress which would skew results, but for now if you want to vote for "faster builds" please post it as a comment and I'll manually add those comments up since that seems to be a popular request.]
Take Our Poll
Filed under: C++
Read More »
Casablanca: C++ on Azure
I've blogged about Casablanca before. Here's a related talk from TechEd Australia:
Casablanca is a Microsoft incubation effort to support cloud-based client-server communication in native code using a modern asynchronous C++ API design. Think of it as Node.js, but using C++ – from simple services, to JSON and REST, to Azure storage and deployment, and more.
Casablanca gives you the power to use existing native C++ libraries and code to do awesome things on the cloud server. In this talk from TechEd Australia, John Azariah and Mahesh Krishnan show how it's done.
Filed under: C++, Cloud, Microsoft, Talks & Events
Read More »
C&B 2012 panel posted: Ask Us Anything!
The second panel from C++ and Beyond 2012 is now available on Channel 9:
Alexandrescu, Meyers and Sutter – Ask Us Anything
Here is the "Ask Us Anything" panel from C++ and Beyond 2012.
Andrei Alexandrescu, Scott Meyers and Herb Sutter take questions from attendees. As expected, great questions and answers…
Table of contents (click the time codes ([xx:xx]) to hear the answers…):
message passing primitives in future versions of the standard… [00:00]
standardized unit testing framework… [02:55]
std::async… [04:30]
standard modules proposal… [08:14]
keyword additions and the standard library… [09:35]
problems (and solutions) with exceptions… [12:50]
future of concepts… [22:34]
std::thread and thread interruption… [23:03]
when to use the auto keyword (and when not to…)… [25:03]
more on auto (benefits of reduncancy, type conversion issues with bool to int?)… [29:31]
const and multithreaded programming, in C++11 const means thread safe, too… [35:00]
yet more on auto (impact on rampant use and code readability/comprehension)… [42:42]
compiler type deduction information (compiler switch that prints out auto deduced type information)… [50:18]
printing out code for review that replaces auto with the actual type… [53:30]
auto and dynamic memory allocation… [54:59]
useful, broadly-used concurrency libraries… [57:00]
Filed under: C++, Talks & Events
Read More »
VC++ 2012 Desktop Express (Free)
Today Microsoft released another free Express version of Visual C++ 2012. In addition to the free Express Visual C++ compiler for building tablet applications, Visual Studio Express 2012 for Windows Desktop directly supports traditional Windows and command-line applications in C++.
This a great free C++ compiler on Windows for everything from hobby development to using and contributing to open source projects. Besides additional C++11 standards conformance with range-for, override and final on the language side (with more to come in the coming months; watch this space) and a complete C++11 standard library implementation, the free compiler also includes unit testing framework for C++, code analysis for C++ (try /analyze today if you haven't already, as John Carmack says so well), C++ AMP for GPGPU programming, and much more.
See also the longer announcement here.
Filed under: C++, Microsoft
Read More »
Reader Q&A: How to write a CAS loop using std::atomics
The following is not intended to be a complete treatise on atomics, but just an answer to a specific question.
A colleague asked:
How should one write the following "conditional interlocked" function in the new C++ atomic<> style?
// if (*plValue >= 0) *plValue += lAdd ; return the original value LONG MpInterlockedAddNonNegative(__inout LONG volatile* plValue, __in LONG const lAdd) { LONG lValue = 0; for (;;) { lValue = *plValue; // volatile plValue suppress compile optimizations in which
// lValue is optimized out hence MT correctness is broken if (lValue < 0) break; if (lValue == InterlockedCompareExchange(plValue, lValue + lAdd, lValue)) { break; } } return lValue; }
Note: ISO C/C++ volatile is not for inter-thread communication,[*] but this is legacy code that predates std::atomics and was using a combination of platform-specific volatile semantics and Windows InterlockedXxx APIs.
The answer is to use a CAS loop (see code at top), which for std::atomics is spelled compare_exchange:
Use compare_exchange_weak by default when looping on this which generally naturally tolerates spurious failures.
Use compare_exchange_strong for single tests when you generally don't want spurious failures.
Usage note: In the code at top we save an explicit reload from 'a' in the loop because compare_exchange helpfully (or "helpfully" – this took me a while to discover and remember) stores the actual value in the 'expected' value slot on failure. This actually makes loops simpler, though some of us are still have different feelings on different days about whether this subtlety was a good idea… anyway, it's in the standard.
For the std::atomic version, roughly (compiling in my head), and generalizing to any numeric type just because I'm in the habit, and renaming for symmetry with atomic<T>::fetch_add(), I think this is what you want:
template<typename T> T fetch_add_if_nonnegative( std::atomic<T>& a, T val ) { T old = a; while( old >= 0 && !a.compare_exchange_weak( old, old+val ) ) { } return old; }
Because the only test in your loop was to break on negative values, it naturally migrated into the loop condition. If you want to do more work, then follow the general pattern which is the following (pasting from the standard, 29.6.5/23 – and note that the explicit ".load()" is unnecessary but some people including the author of this clause of the standard prefer to be pedantically explicit :) ):
[ Example: the expected use of the compare-and-exchange operations is as follows.
The compare-and-exchange operations will update expected when another iteration of the loop is needed.
expected = current.load();
do {
desired = function(expected);
} while (!current.compare_exchange_weak(expected, desired));
—end example ]
So the direct implementation of your function in the general pattern would be:
T old = a; do { if( old < 0 ) break; } while(!a.compare_exchange_weak( old, old+val ) )
but since that easily moves into the loop test I just did this instead in the code at top:
T old = a; while( old >= 0 && !a.compare_exchange_weak( old, old+val ) ) { }
and hoping that no one will discover and point out that I've somehow written a subtle bug by trying to make the code cuter just before leaving for a holiday weekend.
[*] Here's the difference between ISO C/C++ volatile vs. std::atomic<T>/atomic_T: ISO C/C++ volatile is intended to be used only for things like hardware access and setjmp/longjmp safety, to express that the variable is in storage that is not guaranteed to follow the C++11 memory model (e.g., the compiler can't make any assumptions about it). It has nothing to do with inter-thread communication – the proper tool for that is std::atomic<T> which for C compatibility can also be spelled atomic_T (note that in Java and C# this is called volatile which adds to the confusion). For more, see my article "volatile vs. volatile" and Hans Boehm's ISO C++ paper "Should volatile Acquire Atomicity and Thread Visibility Semantics?".
Filed under: C++, Concurrency
Read More »
C&B Panel: Alexandrescu, Meyers, Sutter on Static If, C++11, and Metaprogramming
The first panel from C++ and Beyond 2012 is now available on Channel 9:
On Static If, C++11 in 2012, Modern Libraries, and Metaprogramming
Andrei Alexandrescu, Scott Meyers, Herb Sutter
Channel 9 was invited to this year’s C++ and Beyond to film some sessions (that will appear on C9 over the coming months!)…
At the end of day 2, Andrei, Herb and Scott graciously agreed to spend some time discussing various modern C++ topics and, even better, answering questions from the community. In fact, the questions from Niners (and a conversation on reddit/r/cpp) drove the conversation.
Here’s what happened…
[more]
Filed under: C++, Concurrency, Software Development, Talks & Events
Read More »
"Strong" and "weak" hardware memory models
In Welcome to the Jungle, I predicted that "weak" hardware memory models will disappear. This is true, and it's happening before our eyes:
x86 has always been considered a "strong" hardware memory model that supports sequentially consistent atomics efficiently.
The other major architecture, ARM, recently announced that they are now adding strong memory ordering in ARMv8 with the new sequentially consistent ldra and strl instructions, as I predicted they would. (Actually, Hans Boehm and I influenced ARM in this direction, so it was an ever-so-slightly disingenuous prediction…)
However, at least two people have been confused by what I meant by "weak" hardware memory models, so let me clarify what "weak" means – it means something different for hardware memory models and software memory models, so perhaps those aren't the clearest terms to use.
By “weak (hardware) memory model” CPUs I mean specifically ones that do not natively support efficient sequentially consistent (SC) atomics, because on the software side programming languages have converged on “sequential consistency for data-race-free programs” (SC-DRF, roughly aka DRF0 or RCsc) as the default (C11, C++11) or only (Java 5+) supported software memory model for software. POWER and ARMv7 notoriously do not support SC atomics efficiently.
Hardware that supports only hardware memory models weaker than SC-DRF, meaning that they do not support SC-DRF efficiently, are permanently disadvantaged and will either become stronger or atrophy. As I mentioned specifically in the article, the two main current hardware architectures with what I called “weak” memory models were current ARM (ARMv7) and POWER:
ARM recently announced ARMv8 which, as I predicted, is upgrading to SC acquire/release by adding new SC acquire/release instructions ldra and strl that are mandatory in both 32-bit and 64-bit mode. In fact, this is something of an industry first — ARMv8 is the first major CPU architecture to support SC acquire/release instructions directly like this. (Note: That's for CPUs, but the roadmap for ARM GPUs is similar. ARM GPUs currently have a stronger memory model, namely fully SC; ARM has announced their GPU future roadmap has the GPUs fully coherent with the CPUs, and will likely add "SC load acquire" and "SC store release" to GPUs as well.)
It remains to be seen whether POWER will adapt similarly, or die out.
Note that I’ve seen some people call x86 “weak”, but x86 has always been the poster child for a strong (hardware) memory model in all of our software memory model discussions for Java, C, and C++ during the 2000s. Therefore perhaps “weak” and “strong” are not useful terms if they mean different things to some people, and I’ve updated the WttJ text to make this clearer.
I will be discussing this in detail in my atomic<> Weapons talk at C&B next week, which I hope to make freely available online in the near future (as I do most of my talks). I'll post a link on this blog when I can make it available online.
Filed under: C# / .NET, C++, Concurrency, Hardware, Java, Talks & Events
Read More »
Late-Breaking C&B Session: A Special Announcement
At the end of the Monday afternoon session, I will be making a special announcement related to Standard C++ on all platforms. Be there to hear the details, and to receive an extra perk that's being reserved for C&B 2012 attendees only.
Note: We sometimes record sessions and make them freely available online via Channel 9, and we intend to do that again this year for some selected sessions. However, this session is for C&B attendees only and will not be recorded.
Registration is open until Wednesday and the event is pretty full but a few spaces are still available. I'm looking forward to seeing many of you there for a top-notch C++ conference full of fresh new current material – I've seen Andrei's and Scott's talk slides too, and I think this C&B is going to be the best one yet.
You'll leave exhausted, but with a full brain and quite likely a big silly grin as you think about all the ways to use the material right away on your current project back home.
Filed under: C++, Software Development, Talks & Events
Read More »
C&B Session: atomic<> Weapons – The C++11 Memory Model and Modern Hardware
Here's another deep session for C&B 2012 on August 5-8 – if you haven't registered yet, register soon. We got a bigger venue this time, but as I write this the event is currently almost 75% full with five weeks to go.
I know, I've already posted three sessions and a panel. But there's just so much about C++11 to cover, so here's a fourth brand-new session I'll do at C&B 2012 that goes deeper on its topic than I've ever been willing to go before.
atomic<> Weapons: The C++11 Memory Model and Modern Hardware
This session in one word: Deep.
It's a session that includes topics I've publicly said for years is Stuff You Shouldn't Need To Know and I Just Won't Teach, but it's becoming achingly clear that people do need to know about it. Achingly, heartbreakingly clear, because some hardware incents you to pull out the big guns to achieve top performance, and C++ programmers just are so addicted to full performance that they'll reach for the big red levers with the flashing warning lights. Since we can't keep people from pulling the big red levers, we'd better document the A to Z of what the levers actually do, so that people don't SCRAM unless they really, really, really meant to.
This session covers:
The facts: The C++11 memory model and what it requires you to do to make sure your code is correct and stays correct. We'll include clear answers to several FAQs: "how do the compiler and hardware cooperate to remember how to respect these rules?", "what is a race condition?", and the ageless one-hand-clapping question "how is a race condition like a debugger?"
The tools: The deep interrelationships and fundamental tradeoffs among mutexes, atomics, and fences/barriers. I'll try to convince you why standalone memory barriers are bad, and why barriers should always be associated with a specific load or store.
The unspeakables: I'll grudgingly and reluctantly talk about the Thing I Said I'd Never Teach That Programmers Should Never Need To Now: relaxed atomics. Don't use them! If you can avoid it. But here's what you need to know, even though it would be nice if you didn't need to know it.
The rapidly-changing hardware reality: How locks and atomics map to hardware instructions on ARM and x86/x64, and throw in POWER and Itanium for good measure – and I'll cover how and why the answers are actually different last year and this year, and how they will likely be different again a few years from now. We'll cover how the latest CPU and GPU hardware memory models are rapidly evolving, and how this directly affects C++ programmers.
Coda: Volatile and "compiler-only" memory barriers. It's important to understand exactly what atomic and volatile are and aren't for. I'll show both why they're both utterly unrelated (they have exactly zero overlapping uses, really) and yet are fundamentally related when viewed from the perspective of talking about the memory model. Also, people keep seeing and asking about "compiler-only" memory barriers and when to use them – they do have a valid-though-rare use, but it's not the use that most people are trying to use them for, so beware!
For me, this is going to be the deepest and most fun C&B yet. At previous C&Bs I've spoken about not only code, but also meta topics like design and C++'s role in the marketplace. This time it looks like all my talks will be back to Just Code. Fun times!
Here a snapshot of the list of C&B 2012 sessions so far:
Universal References in C++11 (Scott)
You Don't Know [keyword] and [keyword] (Herb)
Convincing Your Colleagues (Panel)
Initial Thoughts on Effective C++11 (Scott)
Modern C++ = Clean, Safe, and Faster Than Ever (Panel)
Error Resilience in C++11 (Andrei)
C++ Concurrency – 2012 State of the Art (and Standard) (Herb)
C++ Parallelism – 2012 State of the Art (and Standard) (Herb)
Secrets of the C++11 Threading API (Scott)
atomic<> Weapons: The C++11 Memory Model and Modern Hardware (Herb)
It'll be a blast. I hope to see many of you there. Register soon.
Filed under: C++, Hardware, Software Development, Talks & Events
Read More »
Reader Q&A: Why don't modern smart pointers implicitly convert to *?
Today a reader asked a common question:
Why doesn’t unique_ptr (and the ilk) appear to have an operator overload somewhat as follows:
operator T*() { return get(); };
The reason I ask is because we have reams of old code wanting raw pointers (as function parms), and I would like to replace the outer layers of the code which deal with the allocation and deallocation with unique_ptrs without having to either ripple unique_ptrs through the entire system or explicitly call .get() every time the unique_ptr is a parm to a function which wants a raw pointer.
What my programmers are doing is creating a unique_ptr and immediately using get() to put it into a local raw pointer which is used from then on. Somehow that doesn’t feel right, but I don’t know what would be the best alternative.
In the olden days, smart pointers often did provide the convenience of implicit conversion to *. It was by using those smart pointers that we learned it caused more problems than it solves, and that requiring people to write .get() was actually not a big deal.
For an example of the problems of implicit conversions, consider:
unique_ptr p( new widget ); ... use( p + 42 ); // error (maybe he meant "*p + 42"?) // but if implicit conversion to * were allowed, would silently compile -- urk ... delete p; // error // but if implicit conversion to * were allowed, would silently compile -- double urk
For more, see also Andrei's Modern C++ Design section 7.7, "Implicit Conversion to Raw Pointer Types."
However, this really isn't as bad as most people fear for several reasons, including but not limited to:
The large majority of uses of the smart pointer, such as calling member functions on the object (e.g., p->foo()) just work naturally and effortlessly because we do have operator->.
You rarely if ever need to say unique_ptr on a local variable, because C++11's auto is your friend – and "rarely" becomes "never" if you use make_unique which is described here and should become standard in the future.
Parameters (which you mention) themselves should almost never be smart pointers, but should be normal pointers and references. So if you're managing an object's lifetime by smart pointer, you do write .get() – but only once at the top of each call tree. More on this in the current GotW #105 – solution coming soon, watch this space.
Filed under: C++, Reader Q&A
Read More »
Talk Video: Welcome to the Jungle (60 min version + Q&A)
While visiting Facebook earlier this month, I gave a shorter version of my "Welcome to the Jungle" talk, based on the eponymous WttJ article. They made a nice recording and it's now available online here:
Facebook Engineering
Title: Herb Sutter: Welcome to the Jungle
In the twilight of Moore’s Law, the transitions to multicore processors, GPU computing, and HaaS cloud computing are not separate trends, but aspects of a single trend—mainstream computers from desktops to ‘smartphones’ are being permanently transformed into heterogeneous supercomputer clusters. Henceforth, a single compute-intensive application will need to harness different kinds of cores, in immense numbers, to get its job done. — The free lunch is over. Now welcome to the hardware jungle.
The slides are available here. (There doesn't seem to be a link to the slides on the page itself as I write this.)
For those interested in a longer version, in April I gave a 105-minute + Q&A version of this talk in Kansas City at Perceptive, also available online where I posted before.
A word about "cluster in a box"
I should have remembered that describing a PC as a "heterogeneous cluster in a box" is a big red button for people, in particular because "cluster" implies "parts can fail and program should continue." So in the Q&A, one commenter made the point that I should have mentioned reliability is an issue.
As I answered there, I half agree – it's true but it's only half the story, and it doesn't affect the programming model (see more below). One of the slides I omitted to shorten this version of the talk highlighted that there are actually two issues when you go from "Disjoint (tightly coupled)" to "Disjoint (loosely coupled)": reliability and latency, and both are important. (I also mentioned this in the original WttJ article this is based on; just search for "reliability.")
Even after the talk, I still got strong resistance along the lines that, 'no, you obviously don't get it, latency isn't a significant issue at all, reliability is the central issue and it kills your argument because it makes the model fundamentally different.' Paraphrasing subsequent email:
'A fundamental difference between distributed computing and single-box multiprocessing is that in the former case you don’t know whether a failure was a communication failure (i.e. the task was completed but communication failed) or a genuine failure to carry the task. (Hence all complicated two-phase commit protocols etc.) In contrast, in a single-box scenario you can know the box you’re on is working.'
Let me respond further to this here, because clearly these guys know more about distributed systems than I do and I'm always happy to be educated, but I also think we have a disconnect on three things asserted above: It is not my understanding that reliability is more important than latency, or that apps have to distinguish comms failures from app exceptions, or that N-phase commit enters the picture.
First, I don't agree with the assertion that reliability alone is what's important, or that it's more important than latency, for the following reason:
You can build reliable transports on top of unreliable ones. You do it through techniques like sequencing, redundancy, and retry. A classic example is TCP, which delivers reliable communications over notoriously- and deliberately-unreliable IP which can drop and reorder packets as network nodes and communications paths keep madly appearing and reappearing like a herd of crazed Cheshire cats. We can and do build secure reliable global banking systems on that.
Once you do that, you have turned a reliability issue into a performance (specifically latency) issue. Both reliability and latency are key issues when moving to loosely-coupled systems, but because you can turn the first into the second, it's latency that is actually the more fundamental and important one – and the only one the developer needs to deal with.
For example, to use compute clouds like Azure and AWS, you usually start with two basic pieces:
the queue(s), which you use to push the work items out/around and results back/around; and
an elastic set of compute nodes, each of which pulls work items from the queue and processes them.
What happens when you encounter a reliability problem? A node can pull a work item but fail to complete it, for example if the node crashes or the system encounters a partial network outage or other communication problem.
Many modern systems already automatically recover and have another node re-pull the same work item to make sure each work item gets done even in the face of partial failures. From the app’s point of view, such failures just manifest as degraded performance (higher latency or time-to-solution) and therefore mainly affect the granularity of parallel work items – they have to be big enough to be worth sending elsewhere and so minimum size is directly proportional to latency so that the overheads do not dominate. They do not manifest as app-visible failures.
Yes, the elastic cloud implementation has to deal with things like network failures and retries. But no, this isn't your problem; it's not supposed to be your job to implement the elastic cloud, it's supposed to be your job just to implement each node's local logic and to create whatever queues you want and push your work item data into them.
Aside: Of course, as with any retry-based model, you have to make sure that a partly-executed work item doesn't expose any partial side effects it shouldn't, and normally you prevent that by doing the work in a transaction and rolling it back on failure, or in the extreme (not generally recommended but sometimes okay) resorting to compensating writes to back out partial work.
That covers everything except the comment about two-phase commit: Citing that struck me as odd because I haven't heard much us of that kind of coupled approach in years. Perhaps I'm misinformed, but my impression of 2- or N-phase commit protocols was that they have some serious problems:
They are inherently nonscalable.
They increase rather than decrease interdependencies in the system – even with heroic efforts like majority voting and such schemes that try to allow for subsets of nodes being unavailable, which always seemed fragile to me.
Also, I seem to remember that NPC is a blocking protocol, which if so is inherently anti-concurrency. One of the big realizations in modern mainstream concurrency in the past few years is that Blocking Is Nearly Always Evil. (I'm looking at you, future.get(), and this is why the committee is now considering adding the nonblocking future.then() as well.)
So my impression is that these were primarily of historical interest – if they are still current in modern datacenters, I would appreciate learning more about it and seeing if I'm overly jaded about N-phase commit.
Filed under: Cloud, Concurrency, Hardware, Software Development, Talks & Events
Read More »
GotW #105: Smart Pointers, Part 3 (Difficulty: 7/10)
JG Question
1. What are the performance and correctness implications of the following function declaration? Explain.
void f( shared_ptr<widget> );
Guru Question
2. A colleague is writing a function f that takes an existing object of type widget as a required input-only parameter, and trying to decide among the following basic ways to take the parameter (omitting const):
void f( widget& ); void f( unique_ptr<widget> ); void f( unique_ptr<widget>& ); void f( shared_ptr<widget> ); void f( shared_ptr<widget>& );
Under what circumstances is each appropriate? Explain your answer, including where const should or should not be added anywhere in the parameter type.
(There are other ways to pass the parameter, but we will consider only the ones shown above.)
Filed under: C++, GotW
Read More »
GotW #104: Solution
The solution to GotW #104 is now live.
Filed under: C++, GotW
Read More »
Facebook Folly – OSS C++ Libraries
I've been beating the drum this year (see the last section of the talk) that the biggest problem facing C++ today is the lack of a large set of de jure and de facto standard libraries. My team at Microsoft just recently announced Casablanca, a cloud-oriented C++ library and that we intend to open source, and we're making other even bigger efforts that I hope will bear fruit and I'll be able to share soon. But it can't be just one company – many companies have already shared great open libraries on the past, but still more of us have to step up.
In that vein, I was extremely happy to see another effort come to fruition today. A few hours ago I was at Facebook to speak at their C++ day, and I got to be in the room when Andrei Alexandrescu dropped his arm to officially launch Folly:
Folly: The Facebook Open Source Library
by Jordan DeLong on Saturday, June 2, 2012 at 2:59pm ·
Facebook is built on open source from top to bottom, and could not exist without it. As engineers here, we use, contribute to, and release a lot of open source software, including pieces of our core infrastructure such as HipHop and Thrift.
But in our C++ services code, one clear bottleneck to releasing more work has been that any open sourced project needed to break dependencies on unreleased internal library code. To help solve that problem, today we open sourced an initial release of Folly, a collection of reusable C++ library artifacts developed and used at Facebook. This announcement was made at our C++ conference at Facebook in Menlo Park, CA.
Our primary aim with this ‘foolishness’ is to create a solution that allows us to continue open sourcing parts of our stack without resorting to reinventing some of our internal wheels. And because Folly’s components typically perform significantly faster than counterparts available elsewhere, are easy to use, and complement existing libraries, we think C++ developers might find parts of this library interesting in their own right.
Andrei announced that Folly stood for the Facebook Open Llibrary. He claimed it was a Welsh name, which is even funnier when said by a charming Romanian man.
Microsoft, and I personally, would like to congratulate Facebook on this release! Getting more high-quality open C++ libraries is a Good Thing, and I think it is safe to say that Casablanca and Folly are just the beginning. There's a lot more coming across our industry this year. Stay tuned.
Filed under: C++, Cloud, Concurrency, Software Development
Read More »
We're hiring (again & more)
The Visual C++ team is looking for a number of people to do great work on C++11, parallelizing/vectorizing, cloud, libraries, and more. All I can say is that there's a lot of cool stuff in the pipeline that directly addresses real needs, including things people regularly comment about on this blog that I can't answer specifically yet but will soon.
If you might like to be part of it, here's how – 13 positions right now and more to come as we update this list:
Be What's Next (We're hiring!)
The C++ organization is growing and hiring across all feature areas (C++ 11, compiler front-end, compiler back-end, C++ AMP, PPL, libraries & runtime, IDE, Casablanca). We are looking for passionate program managers, developers and testers to bang out the next versions of the toolset!
What's in it for you:
Be part of the C++ standards evolution – you'll have the opportunity to work side-by-side with folks like Herb Sutter
Solve exciting challenges as we navigate the hardware evolution (newer chipsets, multi-core, GPU, heterogeneous cores etc.)
Be part of the technology that builds all of Microsoft's platforms like Windows, Xbox, Windows Phone and Windows Embedded.
Please apply directly using the links below. We'll keep this list updated for the next couple of months.
Current job list is available on that page.
Filed under: C++, Microsoft
Read More »
Two Sessions: C++ Concurrency and Parallelism – 2012 State of the Art (and Standard)
It's time for, not one, but two brand-new, up-to-date talks on the state of the art of concurrency and parallelism in C++. I'm going to put them together especially and only for C++ and Beyond 2012, and I'll be giving them nowhere else this year:
C++ Concurrency – 2012 State of the Art (and Standard)
C++ Parallelism – 2012 State of the Art (and Standard)
And there's a lot to tell. 2012 has already been a busy year for the pushing the boundaries of both "shipping-and-practical" and "proto-standard" concurrency and parallelism in C++:
In February, the spring ISO C++ standards meeting saw record attendance at 73 experts (normal is 50-55), and spent the full week primarily on new language and library proposals, with notable emphasis on the area of concurrency and parallelism. There was so much interest that I formed four Study Groups and appointed chairs: the largest on concurrency and parallelism (SG1, Hans Boehm), and three others on modules (SG2, Doug Gregor), filesystem (SG3, Beman Dawes), and networking (SG4, Kyle Kloepper).
Three weeks ago, we hosted another three-day face-to-face meeting for SG1 and SG4 – and at nearly 40 people the SG1 attendance rivaled that of a normal full ISO C++ meeting, with a who's-who of the world's concurrency and parallelism experts in attendance and further proposal presentations from companies like IBM, Intel, and Microsoft. There was so much interest that I had to form a new Study Group 5 for Transactional Memory (SG5), and appointed Michael Wong of IBM as chair.
Over the summer, we'll all be working on updated proposals for the October ISO C++ meeting in Portland.
Things are heating up, and we're narrowing down which areas to focus on.
I've spoken and written on these topics before. Here's what's different about these talks:
Brand new: This material goes beyond what I've written and taught about before in my Effective Concurrency articles and courses.
Cutting-edge current: It covers the best-practices state of the art techniques and shipping tools, and what parts of that are standardized in C++11 already (the answer to that one may surprise you!) and what's en route to near-term standardization and why, with coverage of the latest discussions.
Mainstream hardware – many kinds of parallelism: What's the relationship among multi-core CPUs, hardware threads, SIMD vector units (Intel SSE and AVX, ARM Neon), and GPGPU (general-purpose computation on GPUs, which I covered at C++ and Beyond 2011)? Which are most interesting, what technologies are available now, and what's being considered for near-term standardization?
Blocking vs. non-blocking: What's the difference between blocking and non-blocking styles, why on earth would you care, which kinds does C++11 support, and how are we looking at rounding it out in C++1y?
Task and data parallelism: What's the difference between task parallelism and data parallelism, which kind of of hardware does each allow you to exploit, and why?
Work stealing: What's the difference between thread pools and work stealing, what are the major flavors of work stealing, which of these (if any) does C++11 already support and is already shipping on some advanced commercial C++ compilers today (this answer will likely surprise you), and what needs to be done in the next round for a complete state-of-the-art parallelism story in C++1y?
The answers all matter to you – even the ones not yet in the C++ standard – because they are real, available in shipping products, and affect how you design your software today.
This will be a broad and deep dive. At C++ and Beyond 2011, the attendees (audience!) included some of the world's leading experts on parallelism and compilers. At these sessions of C&B 2012, I expect anyone who wasn't personally at the SG1 meeting this month, even world-class experts, will learn something new in these talks. I certainly did, and that's why I'm motivated to turn the information into talks and share. This isn't just cool stuff – it's important and useful in production code today.
I hope to see many of you at C&B 2012. I'm excited about these topics, and about Scott's and Andrei's new material – you just can't get this stuff anywhere else.
Asheville is going to be blast. I can't wait.
Herb
P.S.: I haven't seen this much attention and investment in C++ since last century – C++ conferences at record numbers, C++ compiler investments by the biggest companies in the industry (e.g., Clang), and much more that we've seen already…
… and a little bird tells me there's a lot more major C++ news coming this year. Stay tuned, and fasten your seat belts. 2012 ain't done yet, not by a long shot, and I'll be able to say more about C++ as a whole (besides the specific topics mentioned above) for the first time at C&B in August. I hope to see you there.
FYI, C&B is already over 60% full, and early bird registration ends this Friday, June 1 – so register today.
Filed under: C++, Concurrency, Effective Concurrency, Hardware, Software Development, Talks & Events
Read More »
VC++ and Win8 Metro apps: May 18, livestream and on-demand
Reblogged from Sutter's Mill:
Want to know how to write cool tablet apps using Visual C++?
On May 18, Microsoft is hosting a one-day free technical event for developers who want to write Metro apps for Windows 8 using Visual C++. I'm giving the opening talk, and the rest of the day is full of useful technical information on everything from XAML and DirectX to networking and VC++ compiler flags.
Read more… 367 more words
Day-before reminder: If you are interested in tablet apps using VC++, check out the livestream starting at 9am U.S. Pacific time tomorrow, or come back later to watch the talks on demand.
Read More »
VC++ and Win8 Metro apps: May 18, livestream and on-demand
Want to know how to write cool tablet apps using Visual C++?
On May 18, Microsoft is hosting a one-day free technical event for developers who want to write Metro apps for Windows 8 using Visual C++. I'm giving the opening talk, and the rest of the day is full of useful technical information on everything from XAML and DirectX to networking and VC++ compiler flags.
Please note: This event is Windows-specific, and talks will use both portable ISO C++ as well as Visual C++-specific libraries and compiler extensions; for brevity these are being referred to as "C++" to highlight to a Microsoft-specific audience that that this day is about Visual C++, not about Visual C# or Visual Basic or JavaScript.
From the page:
Join the Microsoft Visual C++ and Windows teams in Redmond on May 18, 2012 for a free, all-day event focused on building Windows 8 Metro style apps with C++.
These Windows-specific talks will use both portable ISO C++ and Visual C++-specific compiler extensions; for brevity below we’ll refer to both as "C++" (i.e., this day is about Visual C++, not Visual C# or JavaScript).
We will have pragmatic advice for every developer writing Metro style apps and games with XAML and/or DirectX and C++.
Register now!
Agenda:
Visual C++ for Windows 8, Keynote by Herb Sutter
Building Windows 8 apps with XAML and C++
Building Windows 8 games with DirectX and C++
Introduction to the Windows Runtime Library (WRL)
Writing Connected apps: Writing networking code with C++
Combining XAML & DirectX in a Metro style apps
Writing WinRT components to be consumed from any language
VC11 compiler flags for getting the most out of C++
All sessions will be recorded and available for on demand viewing on C9.
I wish I'd blogged about it right away when it was announced a week or so ago, because registration filled immediately before I could blog it (I think on the first day), and then when the room was expanded it filled again right away again before I could blog about it. Then I procrastinated for a few days. You can still register here for the waitlist to see it in person, but I have good news…
All sessions will be broadcast livestream and then available for viewing on demand. If you're halfway around the world, or just halfway across the country, it's hard to fly somewhere for a one-day event anyway; thanks to livestream and on-demand, the Internet is our friend. I look forward to seeing and e-seeing many of you there.
Filed under: C++, Microsoft, Software Development, Talks & Events
Read More »
Looking for compiler engineers
Are you a compiler engineer or know one, and looking for interesting work on a top-notch team?
We're hiring. (That particular link says two openings, but there are more.)
Filed under: C++, Microsoft
Read More »
Reader Q&A: What about VC++ and C99?
I occasionally get asked about whether, or how well, Visual C++ supports C99.
This week, I just posted two replies to this questions on UserVoice (merged below).
Last fall, I also answered it in an interview with Dr. Dobb's (recommended for some rationale discussion).
The short answer is that Visual C++'s focus is to support ISO C code that is supported by ISO C90 or ISO C++ (98 or 11). For the longer answer, I'm combining my UserVoice answers below, plus an additional comment about restrict in particular.
Our focus in Visual C++ is on making a world-class C++ compiler, and we’re heads-down on C++11 conformance. For C programmers, the good news is twofold:
1. Our primary goal is to support "most of C99/C11 that is a subset of ISO C++98/C++11."
VC++ 2010 already fully supports the C subset of C++98, including things like <stdint.h> and declarations in the middle of a block.[*] The C subset of C++98 is approximately C95 (with very few incompatibilities with C95; i.e., there are very few cases where legal C95 code has a different meaning or is invalid in C++98) plus a few C99 features like declaring variables in the middle of blocks).
VC++11 now in beta already adds partial support for the C11 subset of C++11 (e.g., it supports the new C11 atomic_int types for concurrency and parallelism).
Soon after VC++11 ships we have announced we will do out-of-band releases for additional C++11 conformance which will naturally also include more C11 features that are in the C subset of C++11. We intend to implement all of the C++11 standard, which includes much of C99 — roughly, it includes the C99 preprocessor and library extensions but not the language extensions like restrict.
So we already support large subsets of C99 and some-and-soon-more of C11. Our immediate and long-term goal is to fully support the C subsets of ISO C++.
2. We also for historical reasons ship a C90 compiler which accepts (only) C90 and not C++.
For the (hopefully rare) cases where legal C90 code has a different meaning in C++98 and this matters to C developers, for backward compatibility with older C90 code we also continue to ship a C compiler that implements Standard C90 exactly (using /TC or naming files as something.c).
Granted, however, there is also bad news for C programmers:
3. We do not plan to support ISO C features that are not part of either C90 or ISO C++.
I understand C programmers may be disappointed or angry with this answer and I’m sorry to have to say no here. It’s true, and very quotable, that "focus means saying no," but that doesn’t make it easy to say — it is hard to say no to you, and I’m sorry to say it. But we have to choose a focus, and our focus is to implement (the standard) and innovate (with extensions like everyone but which we also contribute for potential standardization) in C++.
Recommendations
We recommend that C developers use the C++ compiler to compile C code (using /TP if the file is named something.c). This is the best choice for using Visual C++ to compile C code.
Alternatively, we recommend that C developers use the C90 compiler (using /TC or naming files as something.c) if you need to write C90 conforming code that exercises some of the rarer cases that in C++98 are illegal or have changed meaning. This is a fallback primarily intended to support historical C code.
If you really need either of the following:
features in C95/C99/C11 that are not part of ISO C++; or
features in C that are in the C++ subset but without also enabling the writing of C++ code;
then we recommend that you consider using a different compiler such as Intel or gcc (short-term) and/or pressure your standards committee representatives to have ISO C++ include more of the C standard (longer-term).
[*] Visual C++ also partly supports some C99 language features under a slightly different syntax and possibly with slightly different semantics. Notably, we support __restrict – we did (and could again) consider allowing the standard C99 spelling restrict here for this feature, but please understand that this is not as simple as it looks. Not only the VC++ team, but also the ISO C++ standards committee, considered adding restrict to VC++ and ISO C++, respectively. Although it was specifically suggested for ISO C++11, it was rejected, in part because it's not always obvious how it extends to C++ code because C++ is a larger language with more options and we would want to make sure the feature works correctly across the entire language.
Filed under: C++, Reader Q&A
Read More »
C++ Libraries: Casablanca
At GoingNative in February, I emphasized the need for more modern and portable C++ libraries, including for things like RESTful web/cloud services, HTTP, JSON, and more. The goal is to find or develop modern C++ libraries that leverage C++11 features, and then submit the best for standardization.
Microsoft wants to do its part, and here's a step in that direction.
Today I'm pleased to see Soma's post about "C++, Cloud Services, and You" announcing the DevLabs release of Casablanca, a set of C++ libraries for Visual C++ users that start to bring the same modern conveniences already enjoyed by .NET and Node.js and Erlang users also to C++ developers on our local and cloud platforms, including modern C++ libraries for REST, HTTP, and JSON. From Soma's announcement, adding my own emphasis and minor code edits:
Historically, we've lacked such simple tools for developers using C++. While there are multiple relevant native networking APIs (e.g. WinINet, WinHTTP, IXMLHttpRequest2, HTTP Server API), these are not optimized from a productivity perspective for consuming and implementing RESTful cloud services using modern C++. They don't compose particularly well with code based on the standard C++ libraries, and they don't take advantage of modern C++ language features and practices in their programming models.
This is where "Casablanca" comes in. "Casablanca" is a set of libraries for C++ developers, taking advantage of some recent standard language features already available through Visual Studio.
"Casablanca" aims to make it significantly easier for C++ coders to consume and implement RESTful services. It builds on lessons from .NET, from Node.js, from Erlang, and from other influencers to create a modern model that is meant to be easy to program while still being scalable, composable, and flexible.
As an example, here's a snippet that uses the client HTTP library to search Bing for my name and output the results to the console:
http_client bing( L"http://www.bing.com/search" ); bing.request( methods::GET, L"?q=S.Somasegar" ) .then( []( http_response response ) { cout << "HTML SOURCE:" << endl << response.to_string() << endl; }) .wait();
and here's a simple web listener hosted in a console application:
listener::create( argv[1], []( http_request req ) { req.reply( status_codes::OK, "Namaste!" ); }) .listen( []{ fgetc( stdin ); } ) .wait();
For those of you looking to build Azure services in C++, "Casablanca" comes with a Visual Studio wizard to set up everything up correctly. You can target both Web and Worker roles, and you can access Azure storage using the built-in C++ library bindings. […] Taking C++ to the cloud with "Casablanca" is another exciting step in that journey.
Today's Casablanca release is as a DevLabs project, to get usability feedback and to eventually support these features in the full Visual C++ product. If you're interested in using C++ to consume and implement cloud services, and sharing what kind of support you want and whether you think Casablanca is on the right track, please let us know in the forums.
Looking beyond Visual C++, one piece of Casablanca is already being proposed for standardization, namely the "future.then" nonblocking continuations that are required to be able to write highly responsive composable libraries – you really want all async libraries to talk about their work using the same type, and std::future already gives half of what we need (blocking synchronization) and just needs the non-blocking part too. Also being proposed as an optional layer on top of "future.then" is an "await" style of language support to make the async operations as easy to express and use in C++ as in any of the best languages in the world.
Note that there are other C++ libraries too for several of these facilities. Repeating what I said at GoingNative, we (Microsoft) don't care whose libraries get standardized – whether ones we contribute or someone else's. We care most that there be standard C++ libraries for these modern uses, starting with the most basic "future.then" support, and to encourage all companies and groups who have libraries in these important spaces to contribute them and take the best and make them available to C++ developers on all platforms. This is a small step in that process.
Filed under: C++, Concurrency, Microsoft, Software Development
Read More »
World's youngest C++ programmer?
I'm seeing many younger programmers picking up C++. The average age at C++ events over the past year has been declining rapidly as the audience sizes grow with more and younger people in addition to the C++ veterans.
But this one just beats all [Facebook link added]:
A six-year-old child from Bangladesh is hoping to be officially recognised as the world’s youngest computer programmer.
Wasik Farhan-Roopkotha showed an aptitude for computing at an early age and started typing in Microsoft Word at just three years old, BBC News reports.
The precocious youngster was programming game emulators from the age of four and his achievements have already received extensive media coverage in his home country.
He has also gained knowledge of C++, the programming language developed by Danish computer scientist Bjarne Stroustrup, without any formal training.
This kid seems pretty exceptional. Welcome, Wasik! I don't expect the programs to be very complicated, and I'll leave aside questions of balancing computer time with real-world time and exercise, but this is still quite an achievement.
How young were you when you wrote your first program? I discovered computers for the first time at age 11 when I switched to a new school that had a PET 2001, and wrote my first BASIC programs when was 11 or 12, first on paper and then a little on the PET when I could get access to it. I still fondly remember when I finally got my own Atari 800 when I was 13… thanks again for the loan for that, mum and dad! the first loan I ever took, and paid off in a year with paper-route money. Having that computer was definitely worth a year of predawn paper delivery in the rain, sleet, and snow.
Filed under: C++, Opinion & Editorial, Software Development
Read More »
GotW #5: Overriding Virtual Functions
Virtual functions are a pretty basic feature, but they occasionally harbor subtleties that trap the unwary. If you can answer questions like this one, then you know virtual functions cold, and you’re less likely to waste a lot of time debugging problems like the ones illustrated below.
Problem
JG Question
1. What do the override and final keywords do? Why are they useful?
Guru Question
2. In your travels through the dusty corners of your company’s code archives, you come across the following program fragment written by an unknown programmer. The programmer seems to have been experimenting to see how some C++ features worked.
(a) What could be improved in the code’s correctness or style?
(b) What did the programmer probably expect the program to print, but what is the actual result?
#include <iostream>
#include <complex>
using namespace std;
class base {
public:
virtual void f( int );
virtual void f( double );
virtual void g( int i = 10 );
};
void base::f( int ) {
cout << "base::f(int)" << endl;
}
void base::f( double ) {
cout << "base::f(double)" << endl;
}
void base::g( int i ) {
cout << i << endl;
}
class derived: public base {
public:
void f( complex<double> );
void g( int i = 20 );
};
void derived::f( complex<double> ) {
cout << "derived::f(complex)" << endl;
}
void derived::g( int i ) {
cout << "derived::g() " << i << endl;
}
int main() {
base b;
derived d;
base* pb = new derived;
b.f(1.0);
d.f(1.0);
pb->f(1.0);
b.g();
d.g();
pb->g();
delete pb;
}
Filed under: GotW
Read More »
GotW #4 Solution: Class Mechanics
How good are you at the details of writing classes? This item focuses not only on blatant errors, but even more so on professional style. Understanding these principles will help you to design classes that are easier to use and easier to maintain.
Problem
JG Question
1. What makes interfaces “easy to use correctly, hard to use incorrectly”? Explain.
Guru Question
2. You are doing a code review. A programmer has written the following class, which shows some poor style and has some real errors. How many can you find, and how would you fix them?
class complex {
public:
complex( double r, double i = 0 )
: real(r), imag(i)
{ }
void operator+ ( complex other ) {
real = real + other.real;
imag = imag + other.imag;
}
void operator<<( ostream os ) {
os << "(" << real << "," << imag << ")";
}
complex operator++() {
++real;
return *this;
}
complex operator++( int ) {
auto temp = *this;
++real;
return temp;
}
// ... more functions that complement the above ...
private:
double real, imag;
};
Note: This is not intended to be a complete class. For example, if you provide operator++ you would normally also provide operator–. Rather, this is an instructive example to focus on the mechanics of writing correctly the kinds of functions this class is trying to support.
Solution
1. What makes interfaces “easy to use correctly, hard to use incorrectly”? Explain.
We want to enable a “pit of success” where users of our type just naturally fall into good practices—they just naturally write code that is valid, correct, and efficient.
On the other hand, we want to make it hard for our users to get into trouble—we want code that would be incorrect or inefficient to be invalid (a compile time error if possible) or at least inconvenient and hard to write silently so that we can protect the user from unwelcome surprises.
Scott Meyers popularized this guidance. See his concise writeup for further examples.
2. You are doing a code review. A programmer has written the following class, which shows some poor style and has some real errors. How many can you find, and how would you fix them?
This class has a lot of problems—even more than I will show explicitly here. The point of this puzzle was primarily to highlight class mechanics (issues like “what is the canonical form of operator<<?” and “should operator+ be a member?”) rather than point out where the interface is just plain poorly designed. However, I will start off with perhaps the two most useful observation first:
First, this is a code review but the developer doesn’t seem to have tried to even unit-test his code, else he would have found some glaring problems.
Second, why write a complex class when one already exists in the standard library? And, what’s more, when the standard one isn’t plagued with any of the following problems and has been crafted based on years of practice by the best people in our industry? Humble thyself and reuse.
Guideline: Reuse code—especially standard library code—instead of handcrafting your own. It’s faster, easier, and safer.
Perhaps the best way to fix the problems in the complex code is to avoid using the class at all, and use the std::complex template instead.
Having said that, it’s an instructive example, so let’s go through the class as written and fix the problems as we go. First, the constructor:
1. The default constructor is missing.
complex( double r, double i = 0 )
: real(r), imag(i)
{ }
Once we supply a user-written constructor, we suppress the implicit generation of the default constructor. That makes the class annoying to use at all. In this case, we could either default both parameters, or provide a complex() = default; .
Also, as explained in GotW #1, prefer to use { } consistently for initialization rather than ( ) just as a good modern habit. The two mean exactly the same thing in this case, but { } lets us be more consistent, and could catch a few errors during maintenance, such as typos that would invoke double-to-float narrowing conversions.
2. operator+ passes by value.
void operator+ ( complex other ) {
real = real + other.real;
imag = imag + other.imag;
}
This parameter should be passed by const&.
Guideline: Prefer passing read-only parameters by const& instead of by value.
3. operator+ modifies this object’s value.
Instead of returning void, operator+ should return a complex containing the sum and not modify this object’s value. Users who write val1 + val2 and see val1 changed are unlikely to be impressed by these gratuitously weird semantics. As Scott Meyers is wont to say, when writing a value type, “do as the ints do” and follow the conventions of the built-in types.
4. operator+ is not written in terms of operator+= (which is missing).
Really, this operator+ is trying to be operator+=. It should be split into an actual operator+ and operator+=, with the former calling the latter.
Guideline: If you supply a standalone version of an operator (e.g., operator+), always supply an assignment version of the same operator (e.g., operator+=) and prefer implementing the former in terms of the latter. Also, always preserve the natural relationship between op and op= (where op stands for any operator).
Having += is good, because users should prefer using it. Even in the above code, real = real + other.real; should be real += other.real; and similarly for the second line.
Guideline: Prefer writing a op= b instead of a = a op b (where op stands for any operator). It’s clearer, and it’s often more efficient.
The reason why operator+= is more efficient is that it operates on the left-hand object directly and returns only a reference, not a temporary object. On the other hand, operator+ must return a temporary object. To see why, consider the following canonical forms for how operator+= and operator+ should normally be implemented for some type T.
T& T::operator+=( const T& other ) {
//...
return *this;
}
T operator+( const T& a, const T& b ) {
auto temp = a;
temp += b;
return temp;
}
Implementing + in terms of += both makes the code simpler and guarantees consistent semantics as the two functions are less likely to diverge during maintenance.
5. operator+ should not be a member function.
If operator+ is made a member function, as it is here, then it won’t work as naturally as your users may expect when you do decide to allow implicit conversions from other types. Here, an implicit conversion from double to complex makes sense, but with the original class users have an asymmetry: Specifically, when adding complex objects to numeric values, you can write a = b + 1.0 but not a = 1.0 + b because a member operator+ requires a complex (and not a double) as its left-hand argument.
Also, if do you want your users to be able to add complex objects to doubles conveniently, it may make sense to provide overloaded the functions operator+(const complex&, double) and operator+(double, const complex&) too. This works even without implicit conversions to complex.
Finally, the other reason to prefer members is because they provide better encapsulation, as pointed out by Scott Meyers.
Guideline: Prefer these guidelines for making an operator a member vs. nonmember function: unary operators are members; = () [] and -> must be members; the assignment operators (+= –= /= *= etc.) must be members; all other binary operators are nonmembers.
6. operator<< should not be a member function.
The author of this code didn’t really mean to enable the syntax my_complex << cout, did they?
void operator<<( ostream os ) {
os << "(" << real << "," << imag << ")";
}
The same reasons already given to show why operator+ should be a nonmember apply also to operator<<, only more so because a member the first parameter has to be a stream, not a complex. Further, the parameters should be references: (ostream&, const complex &).
Note also that the nonmember operator<< should normally be implemented in terms of a(n often virtual) const member function that does the work, usually named something like print.
7. operator<< should return ostream&.
Further, operator<< should have a return type of ostream& and should return a reference to the stream in order to permit chaining. That way, users can use your operator<< naturally in code like cout << a << b;.
Guideline: Always return stream references from operator<< and operator>>.
8. The preincrement operator’s return type is incorrect.
complex operator++() {
++real;
return *this;
}
Ignoring the sake of argument whether preincrement is meaningful for complex numbers, if the function exists it should return a reference. This lets client code operate more intuitively and avoids needless inefficiency.
Guideline: When you return *this, the return type should usually be a reference.
9. Postincrement should be implemented in terms of preincrement.
complex operator++( int ) {
auto temp = *this;
++real;
return temp;
}
Instead of repeating the work, prefer to call ++*this. See GotW #2 for the full canonical form for postincrement.
Guideline: For consistency, always implement postincrement in terms of preincrement, otherwise your users will get surprising (and often unpleasant) results.
Summary
That’s it. There are other modern C++ features we could apply here, but they would be arguably gratuitous and not appropriate for general recommendations. For example, this is a value type not designed to be inherited from, so we could prevent inheritance by making the class final—but it seems like unreasonable overkill to recommend that all value types should be written with final and claim that violations of that guideline should be considered poor style.
Here’s a corrected version of the class, ignoring design and style issues not explicitly noted above:
class complex {
public:
explicit complex( double r = 0, double i = 0 )
: real{r}, imag{i}
{ }
complex& operator+=( const complex& other ) {
real += other.real;
imag += other.imag;
return *this;
}
complex& operator++() {
++real;
return *this;
}
complex operator++( int ) {
auto temp = *this;
++*this;
return temp;
}
ostream& print( ostream& os ) const {
return os << "(" << real << "," << imag << ")";
}
private:
double real, imag;
};
complex operator+( const complex& lhs, const complex& rhs ) {
auto ret = lhs;
ret += rhs;
return ret;
}
ostream& operator<<( ostream& os, const complex& c ) {
return c.print(os);
}
Acknowledgments
Thanks in particular to the following for their feedback to improve this article: Mikhail Belyaev.
Filed under: GotW
Read More »
No comments:
Post a Comment