Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX

By submitting your information, you agree that devx.com may send you DevX offers via email, phone and text message, as well as email offers about other products and services that DevX believes may be of interest to you. DevX will process your information in accordance with the Quinstreet Privacy Policy.


Bjarne Stroustrup Expounds on Concepts and the Future of C++ : Page 3

Danny Kalev asks Bjarne the hard questions about concepts and C++'s future.




Application Security Testing: An Integral Part of DevOps

Do We Still Need Concepts?

DK: It's no secret that I've never been fond of concepts. However, I can understand and perhaps put up with the concept and requires keywords in C++. They seem pretty intuitive. Why do we need concept maps and axioms? To me they look like theoretical academic exercises that complicate the language unnecessarily. Of course they may have some useful applications, but wouldn't it have been better to start with just the bare minimum and extend concepts cautiously?

BS: Sigh! The exercise is not to minimize the number of keywords. The need revolves around the support of generic programming. To be useful, "concepts" must formalize and support current successful practice. To be successful, concepts must completely support current successful practice. Otherwise, we'll get a messy mix of constrained and unconstrained templates. To be successful, concepts must support a quick transition from "conventional unconstrained" template use to "concepts-based constrained" template use. This, by the way, was one reason I raised concerns about "usability" of the pre-Frankfurt concept design.

Consider concept maps. You need something to map types to concepts. If you don't have that, you can only use a type for a concept if it has been specifically designed with that concept in mind or if you were incredibly lucky. You could argue that for a brand new language with a cohesive user community, you could require a perfect match. However, C++ is a widely used language with a history that goes back to 1972: We have lots of types that predate any concept you might think of and every day we get more as independent developers invent new types and concepts.

Before carrying on, let me just emphasize that today, as over the last decade and more, developers already invent new concepts and write templates that depend on them. Those "concepts" just aren't formalized; they are informal—and usually have incompletely documented requirements and assumptions.

So, we take an existing concept, say a random access iterator, and an existing type, say an int*, and now I want my type to be used as an argument to a template that requires the concept. What can I do? Without concept maps in the language, I have to fake them. In particular, RandomAccessIterator (however spelled or specified) requires a member type called value_type. The template code that uses RandomAccessIterators uses that name as a member name of the argument type. But my int* does not have a member called value_type. In fact, int* does not have any members. Further, Dennis Ritchie did not anticipate the need for any members of pointers in 1972, couldn't have done so without a time machine, and wouldn't have complicated C with a value_type just to address a need that emerged in 1994.

Without concept maps in the language, I have to build my own map: That "map" will be a wrapper class containing an int*, with value_type as a member, and the usual random access operations added. With a bit of luck and a good compiler the result will be as efficient as the original int* and all I have to do is to wrap every use of an int* as a RandomAccessIterator in my new class. If you have done generic programming in C++, you have seen such wrapper classes or the variant of that idea: traits. Such workaround code is common (ubiquitous in generic code). It is also tedious to write, often non-general (i.e. handles only a few cases), and hard for non-experts to penetrate. It is therefore also error prone.

The concept_map facility is a direct solution to this real and fundamental current problem. In particular:

template<class T> concept_map RandomAccessIterator<T*> { typedef T value_type; };

That is, if you use a T* as a RandomAccessIterator then its value_type is T. As usual, we can have a long confused argument about what is the most appropriate syntax, but we need to say something like that, and it is in fact a very common need to "say something like that." There are no general perfect workarounds, so this is a strong candidate for a language feature. I would not like to write generic code using concepts without it [concept maps].

I will not rehash the discussion from "simplifying the use of concepts," but one of my points was that concept maps had become overused and too complicated to use. I made specific proposals to deal with that. However, as a mechanism for mapping types to concepts by adding missing facilities and/or renaming, concepts maps are both necessary and (in my experience) non-controversial.

As described so far, concepts deal exclusively with syntax; they specify that a type must provide certain named operations and types. For example, to be used as a random access iterator, a type must provide *, ++, [], value_type, etc. Naturally, these operations are expected to "do the right thing"—and that "right thing" is documented somewhere (in this case in the C++ standard). Note that this is true whether concepts are language supported or not. All documentation for templates is full of statements about semantic requirements. The more mature that documentation is the more formal and precise those statements are. The question then arises: To which extent should semantic requirements be language supported and how?

The axioms provided a simple syntax for precise semantic statements in the code itself. For example:

concept ForwardIterator<typename X> : InputIterator<X>, Regular<X> { requires Convertible<postincrement_result, const X&>; axiom MultiPass(X a, X b) { if (a == b) *a <=> *b; if (a == b) ++a <=> ++b; } }

This states that a ForwardIterator is an InputIterator with ==, etc. (Regular), where you can do multiple passes over a sequence. The <=> is the equivalence operator, so for ForwardIterators a and b, if a equals b then the effect of *a is equivalent to the effect of *b. Obviously, if you disliked high-school algebra, you'll run away from this screaming, but the predicate logic is a traditional, general, and effective way of specifying meaning.

The axiom design had two main aims:

  • To encourage more precise and comprehensive documentation
  • To provide a way for programmers to provide information for tool builders in the code itself

Many people were confused over the aims of axioms:

  • Some thought that axioms were primarily there to help compilers generate better code. Actually, axioms are not particularly good for that because the types we use in programs don't exactly match mathematical laws: Floating-point numbers such as doubles do not exactly match the rules for reals (e.g. real numbers do not have a NaN) and ints do not exactly follow the rules for integers (mathematical integers do not overflow). Obviously, had offering help to compiler/optimizer writers been a primary aim of axioms, the design would have been a failure.
  • Other people were confused about the nature of axioms: They wanted to prove them for given types. Well, when I learned math, we used axioms exclusively for things we couldn't prove—and I certainly do not propose to add a theorem prover to every C++ compiler.
  • Finally, there were some who wondered if the predicate logic we offered as the notation for semantic rules was sufficiently powerful, and suggested extensions. Predicate calculus has a very long and glorious history, is very expressive, and I certainly wouldn't want to extend it into some transformation language—that really would be invention beyond the state of the art.

So, having stated what axioms are and are not meant to be. Is what is left sufficiently useful? I think so—and the heavy use of axioms in the documentation of concepts seems to support that—but usefulness is hard to demonstrate conclusively.

This is not the place for an exhaustive argument for the utility of axioms (for more information, see N2887). However, I note that some of the trickier problems with the use of concepts come when two concepts are indistinguishable (or just hard to distinguish) by syntactic means (e.g. a forward iterator and an input iterator) and their important differences are semantic. My guess is that my next concept design will rely more heavily on axioms to more directly and simply deal with problems rooted in semantic differences.

Bjarne Stroustrup: There has never been a shortage of claims that C++ was a dead end—often by people busily imitating it.

DK: In your recent DDJ article, you wrote: "'Concepts' were to have been the central new feature in C++0x." Others described them as the "defining feature" of C++0x (N2893). And yet, after the decision to remove concepts, I have the impression that certain committee members downplay their importance in C++0x. For example, Herb Sutter wrote in his blog: "I'm surprised at some of the commentary I've been reading on the web about the deferral of concepts, including not just technical inaccuracies but also that their absence in C++0x is somehow a big deal for C++ as a language…" So, were concepts no big deal after all? If so, why spend so much time and resources on such a feature?

BS: My full quote is:

"'Concepts' were to have been the central new feature in C++0x for putting the use of templates on a better theoretical basis, for firming-up the specification of the standard library, and a central part of the drive to make generic programming more accessible for mainstream use."

That's significantly different from the impression you get from just the initial sentence:

"'Concepts' were to have been the central new feature in C++0x."

Also, I personally wouldn't call concepts "the defining feature" because I don't know what it would mean for a single feature to be "defining" for a language. I suspect that for most people, the most important new feature of C++0x is standard support for concurrency. Like concepts, few programmers will directly use the tasks, atomic types, etc. but most will benefit. Most the improvements in C++0x are aimed at being useful as tools for writing better programs and for building software infrastructures, rather than being spectacular in themselves.

I think that concepts were—and will again—become "a big deal." I consider them central to improving usability of templates, an enabler of large-scale generic programming, and as support for better-quality libraries. That's hugely important over the long term (decades). However, the importance to the average programmer over the next couple of years would probably not be high. Maybe we can put improved concepts in place before too much harm is done—that is before the need for concepts becomes critical for most programmers.

It is important to keep a perspective and remember that concepts were just one of many improvements offered by C++0x. Even without concepts, C++0x will be a significantly better tool for the C++ community than C++98.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



We have made updates to our Privacy Policy to reflect the implementation of the General Data Protection Regulation.
Thanks for your registration, follow us on our social networks to keep up-to-date