The C++14 Standard: What You Need to Know
drdobbs.com
drdobbs.com
Its kind of weird to see people complaining about a C++ feature because its not appropriate to use indiscriminately and requires some discipline -- that's historically been pretty much the whole of C/C++.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n363...:
Of course, using such a function in an expression when
only a forward declaration has been seen is ill-formed:
auto f(); // return type is unknown
int i = f(); // error, return type of f is unknown
And https://isocpp.org/wiki/faq/cpp14-language#generalized-retur...: Of course, this requires the function body to be visible.Then, we do use template parameters already, for which we usually don't know the type. I can't see why we'd bitch about 'auto' and not about the template parameters.
It's just the same thing: coding against interfaces and not implementations.
I prefer to make functions ordinary, and only templatize functions that need to operate on more than one type. There's good reasons for this: the compiler is less effective at type-checking templates, they require the function body to be visible, and they may fail to warn you when passed the wrong type.
"auto" suffers from all those problems, and yet we are being told to use it whenever possible. That's bad advice.
map<unsigned int, map<int, vector<string>::iterator> >::iterator getSomeIterator();
or:
auto getSomeIterator();
Function name and context usually is sufficient to know what SomeIterator refers to, there is no need for horrible STL constructs to try to explain it.
std::map<unsigned int, std::map<int, std::vector<std::string>::iterator> >::iterator getSomeIterator();
I believe you don't need the space between the > > any more, since C++11?
In most cases you can scan over the type ('map blah blah...iterator') but that's not reading and in any case will quickly fail you with nested template types, if you're unfortunate enough to have those.
And whether a function returns an iterator is not consistent even within the STL:
auto loc = container.find("name");
What type is loc? If container is a std::map, then loc is an iterator. If container is a std::string, then loc is a size_t.If you had used the explicit iterator type, you would at least get an error message immediately with the above line, instead of hoping your code does something that size_t doesn't support.
MY_TYPE::iterator getSomeInterator();Anyway, they are talking about documentation. Many people think that having to peek inside a function to know what it returns is worse than having a compiler-checked type declaration. In fact, in most languages with type inference there is still a strong convention to write full signatures for all top level functions, as a form of documentation (return type inference is left for lambdas)
if (!arg1 || !arg2 || !arg3 ...) {
what_I_should_do_here_isnt_really_clear();func_call(&var1);
is most definitely a C++ convention bud, and it has nothing to do with google.
Not only did you need to use the type correctly, but you needed to declare the desire to use a specific type from the get-go.
You have a point there
what you could do is:
return (int)x;
return (YourClass)value;
(then the header and implementation are auto but you still know what the code is talking about
(Or just put the type there as usual)
Maybe such a tool for C++ will also appear?
Requiring pointless verbosity on some hope that somehow that'll help bad programmers write better code seems like an odd conclusion.
An auto return type means the type is inferred from the type of the expression that the function returns, so it's known from that.
Say I have a header file "foo.h" with an "auto foo();" declaration. Another file (eg, "bar.h") imports foo.h. How can the compiler do type checking when compiling bar.cc without also parsing foo.cc to determine the return type of foo()?
To quote:
> Of course, this requires the function body to be visible.
https://isocpp.org/wiki/faq/cpp14-language#generalized-retur...
template<typename Range>
auto begin(Range& r) -> decltype(r.begin()) {
return r.begin();
}
The return type can't be specified by the function[0], and you have to copy and paste the body of the function into the decltype() clause to tell the compiler that the return type is whatever the thing it's wrapping returns. When you have some long and complicated expression rather than just r.begin() in the body, this duplication becomes a major issue.[0] In this specific case you could require that Range have an iterator typedef and return that, but that approach does not generalize.
If someone starts using auto for every variable or returning it from every method, then they need a stern talking-to.
http://herbsutter.com/2013/06/07/gotw-92-solution-auto-varia...
http://herbsutter.com/2013/06/07/gotw-93-auto-variables-part...
http://herbsutter.com/2013/08/12/gotw-94-solution-aaa-style-...
He has some good points, but his perspective seems to be biased by a professional focus on writing libraries and more general-purpose code. He's also brilliant and can remember things like this:
const int& cir = val;
auto h = cir;
//The type of h is int.
It's difficult to make generic rules for how to use C++ (because it's used for everything from adware to nuclear reactors), but I can say that Herb's AAA (Almost Always Auto) rule would not fly on any development team I've ever worked with.Probably you expected |auto| to behave like |decltype()|, C++11's other form of type deduction, which gives you the actual type of an expression. C++14 adds |decltype(auto)|, which lets you declare local variables using this behavior if you want it.
I'm not sure whether I'd apply Herb's AAA rule quite as broadly as he suggests, but I think it's broadly right, because far too often in C++ we write a type twice on the same line, which is just redundant. I vastly prefer |auto val = new MyClass;| to |MyClass val = new MyClass;|, and similarly |auto casted = static_cast<MyType>(uncasted);| is completely readable without typing writing "MyType" a second time. C++ programmers are a conservative lot, but I expect using |auto| at least in those cases to be pretty mainstream within a few years.
It does not matter what the rationale behind the committee's decision was. This is standard practice in duck typed languages, and it will be adopted as well in C++, since it will be (imho, correctly) perceived as another step to bring C++ "to the 21th century".
You can stern-talk as much as you wish... but it won't have much effect unless backed up by an (effectively enforced) internal code style guide.
The barbarians are already at the gate. Which side are you going to pick?
Only because there is no choice in those languages -- and, in those language communities, there's considerable call for finding a good way to include optional explicit and enforced declared typing specifically so that a choice does exist.
So, even given that communities that (unlike C++, even with auto existing) accept languages that don't provide an alternative to to the equivalent of "auto everywhere" push back against that practice to the extent of seeking language features to provide an alternative to it, I don't think that auto becoming an option is going to mean auto everywhere takes over C++.
There'll probably be some overuse (particularly in a brief period before a widely-accepted conventions on appropriate use are established), but its hardly the most dangerously abusable feature of the C/C++ language family.
This is quite a narrow-minded line of thinking. You're saying "Feature X is should be excluded because of <possible consequence>". But we can say the converse about this same feature: "Feature Y should be included because <possible benefit>". Every programming language feature ever has had its pros and cons, and every language designer makes tradeoffs deciding which features to include and which to exclude.[1]
You also most likely overestimate the practical unreadability of the source code. IDEs are (or will be) smart enough to deduce and display the type of an auto declared variable[2] when you want it. For short functions, it will be blindingly obvious what the auto-declared return type of a function is. Using auto will vastly improve readability by reducing textual noise when a function returns (1) heavily templated expressions, (2) any type within a deeply-nested namespace, (3) any type with a long name, or especially (4) combinations of the previous three (e.g. std::vector::const_iterator< std::pair<std::string, double> >). Complicated types are a common issue in languages with static type systems[3].
I could go on. There are tons of use-cases where auto will help out. The biggest complaint I have is that you can auto-declare the return type of methods that return private variables from a class (unless I'm mistaken; I haven't actually tried it). This pokes leaky holes in your class's encapsulation, forming dependencies between external code and your class's private, internal implementation (although this may be possible through template magic...).
The barbarians are already at the gate. Which side are you going to pick?
Oh, cut the drama. Every language has some sort of a style guide, and C++ programmers are already used to following a style guide and programming with discipline. One more feature with a few downsides and a number of benefits is not going to make a difference, and is totally in line with its philosophy[1].
1: http://en.wikipedia.org/wiki/C%2B%2B#Philosophy 2: http://stackoverflow.com/q/19625115 3: https://github.com/rust-lang/rust/issues/10448
Not to detract from your other points, but personally I wouldn't want to live in a world where my IDE is decided for me. A person sometimes has decades of experience with their text editor. Forcing them to use a different one just to do programming isn't necessarily the best idea.
Interesting question: Will the world benefit from a language that requires an IDE to program in? I can imagine how the answer might be "yes," but also other scenarios where it might be "no."
I love the rest of the changes, but that just doesn't sit right with me.
The standard can't use commas as a separator because using commas vs periods to demarcate components is locale specific. Apostrophes look like a reasonable compromise because lots of calculators already use apostrophes to separate digits:
https://www.google.com/search?q=14+digit+calculator&source=l...
Dunno why they didn't use underscores though.
For example, the "->" was already an operator for pointer indirection but it was reused for lambda definitions. The "[]" was already used for array subscript indexes but was reused for lambda captures.
Or, they could have defined C++14 to simply invalidate your example syntax of "x=1,2". As an example, the "auto" keyword was made a "breaking change" such that "for (auto int i = 0;;)" no longer compiles.
The bottom line is that there are a myriad of ways to address (potential) parsing ambiguities when introducing new language features and syntax. (Whether or not a comma digit separator is worth the clumsier syntax of a special prefix or inflicting the pain of a breaking change is a separate concept.)
As an example, your proposed solutions
_1,000,000
k1,000,000
Both are already valid if variables or functions _1 or k1 are in scope, and evaluate to (octal) zero.And I haven't checked, but it would guess that invalidating "x=1,2" in the parser would not be "simple". I cannot think of a reason, but it also might turn out to be more common then one would think due to macro expansions.
I stand corrected.
So instead of 1,234.56789 (English) or 1.234,56789 (French) we do 1 234.56789 (international)
[0-9]+ [0-9]+ has no meaning in C++, so using space for magnitude decorations would have worked.
> ...
> [Code sample that returns -1 or record->id]
> Instead of using an integral type to identify the person in the record object, maybe I have a new GUID type. Making that change to the record object will cause a series of cascading changes in things like the return types of functions. But if my function uses automatic return type deduction, the compiler will silently make the change for me.
Yet when this happens, his example function can now return -1 or a GUID, so compilation should fail, no? Haven't worked with C++ in awhile so I might just be missing something on this, but seems like a contradiction to me.
One thing the C++ standard library IMO needs is a range library, e.g. something like Boost Range. I often would like to use the "range-for" but when I am not looping over all the elements of a container its useless in that case.
I agree about the range library, but it will be difficult to impossible to implement in the standard library without concepts. 2020 could be the year of range algorithms.
template<typename RANGE, typename PRED> void sort(RANGE, PRED);
and
template<typename ITERATOR> void sort(ITERATOR start, ITERATOR stop);
Since we already have the iterator algorithm defined, there is no room for a range algorithm name 'sort' with two template-deduced parameters. There are workarounds using enable_if, but they're hairy and involve basically recreating concepts at the standard library level.
Herb Sutter:
"I’m hopeful that the standard library will get range-based overloads of all standard algorithms that are enable_if’d to avoid the problem or can use concepts if those make it into a future standard."
http://herbsutter.com/2011/10/07/why-no-container-based-algo...
Why concepts then? The concepts version would look like:
template<Range, Comparator> void sort(Range & r, Comparator const & c);
...and the compiler would be able to tell that iterators wouldn't work with this algorithm.
This is also why C++14's dual-range algorithms didn't lead to ambiguities: equal(InIt1, InIt1, InIt2, Pred) versus equal(InIt1, InIt1, InIt2, InIt2) has the same structure.
(Obviously if you have a type that is both a range and a predicate, then it's ambiguous.)
So what are the nuances when comparing this approach to using a template to create a generic function? Aside from stylistic differences, would using one over the other affect compile time or runtime performance in any (meaningful) way?
Since when was a language spec put behind a paywall? I can't comprehend the reasoning behind this, given that open source compilers exist for C++. How much is it to view the spec? Why do they need to charge at all?
here is an explanation http://stackoverflow.com/questions/6815685/move-semantics-wh...
it is supposed to save extra copies like here
string a("a"); string b("b");
string c = a + b; // creates temporary object that is copied into C by value - the temporary is just a 'full copy'; so that for the temporary it needs an extra allocation and strcpy and free.
the fix is to add the following to the string class
string(string &&r) { ... }
string &operator=(string &&r) { ... }
so the operator with the && receives and 'rvalue' reference - which is a special reference to just this temprorary and the implementation of this is supposed to swap the data of the temporary with that of the this object; the temporary is then discarded.
[1] https://github.com/CppCon/CppCon2014/blob/master/Presentatio...
Can you give some examples of methods in the standard library that should use const, but doesn't?
The biggest problem are the pre-C++98 codebases or the ones full with C idioms instead of modern C++.
Those aren't going away anytime soon.
Thanks for pointing that out. THIS is the real problem of C++. Not the language spec (though it can and has to be improved with time), but those codebases, which incidentally make perfectly sane people go nuts about C++... :)
It says:
1. With deduction you always get right type
2. Deduction guarantees no implicit conversion will happen
From this we can conclude that "the right type" never involves an implicit conversion. This is false: see vector<bool>::operator[], or consider the fact that auto allows you to make a variable of a type you cannot name explicitly (i.e. is private), which is surely not "the right type!"
> The primary use-case of auto returns is for templated functions where the return type depends on the thing passed in.
Anyone know if compilers will support that?
Good changes, but too little, too late.
Of course, we still depend on it, but for an ever smaller set of use cases. Do what you have to do and "get out of there" as fast as you can to your favourite language.
Yes, Rust looks VERY promising.
In the first years of C++ it was real joy to write software in it but now the syntax seems to be much more useful for obfuscation rather than maintainability.
Funny: When C++ began Ada 95 looked like a giant animal. Now Ada 2012 looks small and efficient in comparison although it is more powerful than Ada 95.
System programming languages need OS vendors support to thrive.
Support is provided by AdaCore for instance:
The point being systems programming languages die if not sponsored by the OS vendors, because they have to fight for mindshare among the so called business languages.
In Ada's case, the high cost of development systems, rise of UNIX use in the enterprise, decline of Pascal based languages.
All contributed to it being used in its field of origin (avionics) and systems where security and human life are of utmost importance.
In Europe there was a rise of Ada projects due to lack of security in the usual C and C++, but these are always specialized projects, like train systems control and such. Not the typical Show HN applications.
I would like to see Ada being more used in the industry, but sadly it won't happen.
Not necessarily.
http://www.adacore.com/press/gnat-gpl-for-bare-board-arm/
Look also for the book "Building Parallel, Embedded, and Real-Time Applications with Ada" by McCormick, Singhoff and Hugues.
Some prominent embedded engineers wonder why Ada is not used much more:
http://www.embedded.com/electronics-blogs/break-points/40082...
> Rise of UNIX use in the enterprise
Ada is available for Windows, Linux, OSX and ARM. GNAT/GCC is part of many Linux distros (usually Ada95). You can also download Ada2012 and Ada2014 compilers and tools from Adacore's website.
> Decline of Pascal based languages.
Ada looks a bit like Pascal but the language is far superior. In my opinion Ada's syntax makes software easier to maintain than C/Java like syntax. Even after years I have no problem to read into Ada code. C++ code can be difficult to read just after a short time.
I think the real reason of Ada's decline is that it requires a lot of discipline of the developer. The same kind of discipline is required in Haskell. You cannot just sit down and code. You have to think a lot about your design before you hit the first key. Most developers don't like this, they want quick (and dirty) solutions, and they get it with C and C++.
Back when we had to buy compilers for anything we wanted to do.
Ada compilers and hardware requirements were too complex to target home computers, which we were mostly coding in Assembly for performance and space reasons.
On the computer systems able to use Ada compilers, they were either running UNIX or some other mainframe OS.
Maybe you don't know that Rational Software first product was a Ada Machine in the same vein as Lisp Machines, but with Ada.
https://www.ibm.com/developerworks/community/blogs/gradybooc...
On the UNIX systems when you bought the SDK, there was only C compiler there, latter C++ as well. Getting Ada meant shelling out for an extra compiler, now get business to pay for it.
Mainframes like VMS were a bit more friendly to Ada, but if I remember correctly, still you need to pay for each language separately.
So it doesn't matter where Ada is available today, but how its use since the early 80's progressed.
Now it has to fight against established languages. Its use has been increasing in Europe (check FOSDEM Ada talks) mainly thanks to C and C++ security exploits and GNAT Core OSS efforts, but it is a slow progress.
Everyone talks up Ada's safety, but they always seem to compare it to other Foogols, like C, C++, and Java/C#.
You cannot directly compare Ada with Haskell just on safety as they use different paradigms.
Anyway, just from the top of my head:
- Bound checked arrays
- Real strings
- Reference parameters
- Real enumerations without implicit conversions to numeric data types
- Type definitions are real types, not synonyms like C's typedef. Just like Haskell's newtype.
- Ability to define type ranges for enumerations and numeric types
- Subtyping
- Generics
- Explicit type conversions required among numeric types.
- Contracts, Eiffel style
- Pointers by default are always valid, unless your are doing unsafe code.
In terms of performance, there are ways to enable unsafe code, if you really really need it, but like other memory safe systems programming languages you need to be explicit about it.
A plus feature over Haskell is that currently the Ada has primitives for hardware access. The guys doing HaLVM and Home had to add extensions to Haskell.
Which happens to be C++, until OS vendors support another one on their SDKs.
So until Ada, Rust, D, Swift, .NET Native or something else get such a position, C++ it is.
If you write your library in Perl, guess what, only people writing Perl will use it. If you write it in Java, well, maybe the Scala folk will wrap it up, or the Jython folk, but you won't get any people writing in Ruby to use it. If you write it in C++, you can provide efficient bindings to any of those languages.
For an example, look at FFmpeg or ImageMagick. They are libraries that just _won't die_, no matter how hard people have tried. My god, ImageMagick was written in 1987, and it now has Haskell bindings[0]! The only way to get that sort of longevity (and old code is good code, after all [1]) is to write in language that will outlast the ups and downs of the language or framework du jour, and C and C++ have proven to have that staying power. Yeah, they're terrible; I hate them. But there is a reason to use them.
[0] http://hackage.haskell.org/package/imagemagick
[1] http://www.joelonsoftware.com/articles/fog0000000069.html
- Another layer to debug (FFI)
- Possible loss of performance when crossing the FFI barrier
- Loss of productivity, because that cool API isn't available and so one has to write it
- Writing drivers, daemons/services, OS plugins, ...
One of the reasons I moved from Turbo Pascal to C++, when I started coding on Windows 3.0, was being fed up to write binding libraries by myself. Although I really enjoyed Turbo Pascal back then.
This is of course personal preference, but also mostly because I realized (and it seems to me that some people haven't, just go around and ask what is C/C++, and people will tell you it's one language...) that C++ is not just anymore "C with classes", and the fact there is C-ish, or even just bad C++ code out in the wild doesn't make the language bad in itself (it can still be less appropriate/completely irrelevant for some applications, which is fine).
But sorry, I don't miss the includes, the huge class hierarchies, the template madness, etc
C++ is good if you stick to a strict code standard and don't let people "go crazy" with it.
But the thing is that good languages end up limiting the amount of "bad code" that naturally emerges. (Java does it through lack of flexibility and enforcement, I prefer the Python and Go way)