
C++17 Features That Will Make Your Code Simpler - joboccara
https://www.fluentcpp.com/2018/06/19/3-simple-c17-features-that-will-make-your-code-simpler/
======
edanm
Every time I read about modern C++, I'm amazed at how different the language
looks compared to how it was when I last used it heavily, around 10 years ago.
It's had amazing progress, it looks way more "modern" and really seems like a
brand new language.

~~~
pjmlp
While true, many of the modern idioms were already possible in C++98 when you
look at what was being done in OWL, VCL, CSet++ and other similar libraries.

I mean, even RAII was already a thing in MS-DOS C++ code bases.

However, many kept using it as if it was C with some extras.

~~~
mhd
It didn't exactly help that "many" included the platform toolkit authors. MFC
made me look at Motif fondly.

Never mind compiler support. I just recently noticed with delight that I
finally forgot the exact VC #pragma numbers to disable all the STL warnings.
Which gives me hope that one day I'll manage to do the same to ORA-00907.

~~~
pjmlp
Yep, agreed.

MFC wasn't something I would list as good C++ GUI examples.

Unfortunately Borland just made a mess of their customer base.

Even today, Visual C++ is not as visual as C++ Builder.

------
evanpw
My biggest complaint with structured bindings is that you have to give a name
to every element, so if you're compiling with -Wall, you get a lot of "unused
variable" warnings. Similar functionality in other languages lets you use _ to
mean "I don't care", but in C++ that's considered just a regular single-
character variable name.

~~~
cannam
Mine, tentatively, is that the binding is made by declaration order, not by
name:

    
    
       struct { int a; int b; } x { 1, 2 };
       auto [b, a] = x;
    

Now b is 1 and a is 2, even though in the original structure b was 2 and a was
1. This is contrary to how most other languages with this feature behave.

(I wrote a bit about this once:
[https://thebreakfastpost.com/2017/11/15/c17-destructuring-
bi...](https://thebreakfastpost.com/2017/11/15/c17-destructuring-bind/))

I haven't had the opportunity to write any C++17 in production yet, so I'm not
sure how dangerous this dangerous-looking thing actually is.

~~~
HelloNurse
This behaviour is the only reasonable one; "auto [b, a] = x;" cannot be
considered wrong in any theoretical or practical way.

The b and a in "auto [b,a]" are arbitrary names you are applying to the result
of the destructuring assignment, and they are essentially unrelated to the
names of the struct members.

If you write "auto [apples, oranges] = x;" it means exactly the same thing as
[b,a] and [a,b]: two ints, each with an arbitrary identifier, the first with
value x.a and the second with value x.b.

The only unusual and slightly flaky thing that's going on is interpreting a
struct as a sequence; the order of struct fields is usually relevant for its
memory layout, but not for language features that use identifiers.

What you appear to want is something entirely different: constructing
_something_ that automatically mirrors the names of the struct members.

For example Java reflection allows enumerating the fields in a class, getting
their names and other metadata, and extracting from an object the value of a
field of its class. You could then populate a map with a destructured copy of
an object in which each value has the same name as in the original object.

~~~
cannam
Your reply is written as if you think my objection is absurd - then in the
middle of it you concede exactly the thing I'm objecting to, and describe it
as "unusual and slightly flaky"!

I do get what you're saying; I can see why it's done this way. C++ lacks
tuples at the language level, so there is no other way to do an anonymous
tuple-style binding. Being forced to bind to the same name as the structure
element would be inconvenient for various reasons. It makes sense. I just
think it's problematic that changing the declaration order of a struct can
silently break code that uses it. And it _is_ different from other languages -
not just natty ones like ML but also mainstream ones like Javascript ES6 -
which makes it a point of some curiosity.

> What you appear to want is something entirely different: constructing
> something that automatically mirrors the names of the struct members.

It's all just a question of declaring and assigning local variables, there
would be no mysterious objects or runtime reflection involved in either case.

~~~
HelloNurse
No, it doesn't make sense. Order coupling between destructuring assignment
targets and class fields is very similar to order coupling between formal and
actual parameters of functions, which is easier to get wrong and for which no
protection exists.

Struct X could have a constructor

    
    
       public X(int first,int second):a{first},b{second}
    

but a caller intending to set a=1 and b=2 could call X(2,1) instead
(particularly if last time they checked the constructor was public X(int
first,int second):b{first},a{second}). You certainly don't want to prevent
this "mistake" by enforcing that the first parameter must be a local variable
called a and the second must be a local variable called b and that the
constructor parameters must be called a and b too.

This sort of easily detected error is exactly the same as writing [b,a]
instead of [a,b] in a deconstructing assignment.

Treating a sequence of fields in a class as a tuple is "unusual and slightly
flaky" because few and unimportant languages do it, because it's very new for
C++, and because it faces complications like inheritance, not because it is
wrong or bad; someone might be slow to adapt, but everyone else will benefit.

In practice, only very plain and well documented structs will be suitable for
deconstructing assignment; for example, in code generated from data
serialization schemas there is a natural and well known order for class
members.

~~~
cannam
> a caller intending to set a=1 and b=2 could call X(2,1) instead [...] You
> certainly don't want to prevent this "mistake" by enforcing that the first
> parameter must be a local variable called a and the second must be a local
> variable called b and that the constructor parameters must be called a and b
> too.

An Objective-C programmer might disagree with the last part. But of course
it's possible to come up with examples of other things already in C++ that are
equally error-prone. That doesn't mean this new thing won't be error-prone
too.

I bet that many C++ programmers who are passingly familiar with C++17 would
agree that the second line here:

    
    
       struct { int a; int b; } x { 1, 2 };
       auto [a, b] = x;
    

is syntactic sugar for

    
    
       auto a = x.a;
       auto b = x.b;
    

Introductory material about structured bindings routinely uses examples like
this, where the names happen to match those in the structure, without making
it clear that this is not what actually goes on.

> This sort of easily detected error

"Easily detected" in C++ means the compiler detects it. The rest might as well
be Javascript.

> Treating a sequence of fields in a class as a tuple is "unusual and slightly
> flaky" because few and unimportant languages do it

Can you name one? This is one thing I've been trying to discover here, without
success.

> In practice, only very plain and well documented structs will be suitable
> for deconstructing assignment

I do agree with this -- good practice should be to use it only for "obvious"
cases.

I will use this feature, when I get the opportunity. I use element-wise
structure initialisation often enough. It's convenient, this will be
convenient. It also bites me occasionally and so will this.

------
ancarda
I'm _completely_ ignorant about C++, so I will ask this question: What's the
state of memory management in modern C++? Is it easier to avoid shooting
yourself in the foot? Can you write code that is completely safe?

I've always flat-out avoided C++ because I don't think I will be able to
handle memory management for a large program; It'll be riddled with security
holes and memory leaks.

Unfortunately, many OSS programs I want to contribute to are written in
unmanaged languages like C or C++. As far as I'm aware, it's easier to get
memory right in modern C++ compared to C, which hasn't received many updates.

~~~
tzahola
>Can you write code that is completely safe?

Yes. Just don't use raw pointers. Use unique_ptr, shared_ptr and weak_ptr
instead.

~~~
xwvvvvwx
smart pointers are a good start, but completely safe is a pretty big
exaggeration.

Just off the top of my head smart pointers do not protect against:

\- null pointer dereferences

\- out of bounds array access

\- iterator invalidation

\- dangling (non-owning) references

~~~
pjmlp
No, but C++ compilers like Visual Studio do.

[https://blogs.msdn.microsoft.com/vcblog/2017/08/11/c-core-
gu...](https://blogs.msdn.microsoft.com/vcblog/2017/08/11/c-core-guidelines-
checker-in-visual-studio-2017/)

[https://blogs.msdn.microsoft.com/vcblog/2017/06/28/security-...](https://blogs.msdn.microsoft.com/vcblog/2017/06/28/security-
features-in-microsoft-visual-c/)

[https://docs.microsoft.com/en-us/cpp/security/security-
best-...](https://docs.microsoft.com/en-us/cpp/security/security-best-
practices-for-cpp)

------
makecheck
The _code_ examples are great but I also want examples of compiler errors when
things go wrong.

C++ sort of has a reputation for compilers omitting messages that are, to put
it mildly, unhelpful and verbose. While they’ve improved over the years, I’d
like to know if new features have new confusing error messages.

Saving 30 seconds writing a one-liner doesn’t really “count” if there’s a
chance of seeing a 40 line template-unrolling when deduction fails due to a
typo. Does that happen here?

------
mlthoughts2018
What is the intended use of auto in these cases in a statically typed
language? It seems counter-productive because in C++ writing type annotations
is an aide to the code reader to understand the code author’s intent. Making
the code reader also infer types of constituent variables, and remember those
types in their head as they read the surrounding code where some e.g.
structured binding was used to assign to some variables — this seems like a
very bad thing to do to code readers and you’re gaining hardly anything in
terms of terser syntax.

Compared with Python, say, where you always know that the burden of
understanding how a variable is used is part of the reader’s responsibility,
maybe with conventional help in docstrings or optional type hints, this C++
feature seems like the worst of both worlds. You still have to add _some_
annotation (“auto”) and keep in mental cache an understanding of the types,
instead of just being nice and explicit about it and declaring things with the
types.

So you don’t get the freedom of duck typing on the auto’d variables, and you
trade for slightly reduced syntax instead of explicit annotations for the
reader.

It seems like it would immediately become a C++ anti-pattern to use structures
binding. It’s cute instead of pragmatic.

~~~
Game_Ender
This a matter of debate in the C++ community. The “almost always auto” camp
has the position that it makes the code more flexible and easier to refactor.
The fewer times you write a type name the easier it is to change. It also has
its uses in template heavy code which is popular on modern C++.

There is a group such as your self that value the extra context the type
provides. I think the counter to is that modern tooling can provide this
information to you. To split the difference you can use auto just where the
type is redundant, like with make_shared, or verbose and unneeded, like with
iterators.

~~~
mlthoughts2018
It might be easier to change types behind the scenes when using auto, but why
would someone feel that is going to be a common case? When are you completely
changing types all the time?

More often, the types don’t change all that much, but their behaviors get
extended and new features have to be calculated in some block of code. If you
can see the type explicitly, then you know what operations it supports, etc.

If I come across a structured binding where x and y are auto assigned as the
unpacking of struct fields whose types might be complicated, now I have local
variables x and y that I cannot reason about until I backtrack manually to
figure out what type they will be deduced to having (and then keep that in
memory any time I’m tinkering in that section of code).

I guess for the cases when big structural overhauls are happening behind the
scenes to radically change underlying types and designs, auto would reduce a
bunch of rework on type annotations. However, I just can’t believe anyone
would allow that to be a common enough use case for it to matter more than
explicit type annotations.

~~~
susam
> If I come across a structured binding where x and y are auto assigned as the
> unpacking of struct fields whose types might be complicated, now I have
> local variables x and y that I cannot reason about ...

One is free to not use the auto keyword in a scenario like you mention in
order to make the types explicit regardless of what the "Almost Always Auto"
camp says.

However there are scenarios where leaving the type out does not degrade
readability and sometimes improves readability. Often cited examples of
situations where it may improve readability usually look somewhat like this:

    
    
        void print_vector_of_maps(std::vector<std::map<std::string, std::string>> v)
        {
            for (const std::map<std::string, std::string>& item: v) {
                for (const std::pair<std::string, std::string>& entry: item) {
                    std::cout << entry.first << ": " << entry.second << '\n';
                }
                std::cout << "---\n";
            }
        }
    

With the auto keyword, it looks like this:

    
    
        void print_vector_of_maps(std::vector<std::map<std::string, std::string>> v)
        {
            for (const auto& item: v) {
                for (const auto& entry: item) {
                    std::cout << entry.first << ": " << entry.second << '\n';
                }
                std::cout << "---\n";
            }
        }
    

The types are still known from the surrounding context and the readability has
arguably improved.

~~~
mlthoughts2018
The auto example here looks strictly worse than just creating a descriptive
typedef/typename to shorten the syntax of the first approach.

I’m all for shorter syntax, just not at the expense of completely explicit
type annotation.

~~~
xyrouter
The usage of the word "strictly" here is strictly sloppy.

Why is a typedef/typename better than using auto in this case? You can
literally look up the actual type the auto keyword is deducing by looking at
the function parameters two lines above.

How many new typedefs do you want to create for code like this? You are
suggesting creating two typedefs for this tiny 4-line code only. Imagine how
many typedefs you might need to create in a real and complex project. How is
the mess of typedefs going to be any more maintainable than this?

There are places where the auto keyword would make it hard to understand the
type and there are places where it won't. This is a place where the auto
keyword does not make it hard to understand the type.

Don't use the auto keyword if you don't want to but pretending that there is
no value in using the auto keyword by using weasel words such as "strictly" is
disingenuous.

~~~
mlthoughts2018
> “Why is a typedef/typename better than using auto in this case? You can
> literally look up the actual type the auto keyword is deducing by looking at
> the function parameters two lines above.”

Because a typedef makes the syntax equally as short as auto in all practical
senses, while keeping the item type _explicit_. Even when figuring out the
item type only requires looking up a few lines, this limitation of auto is
still worse while not providing any “terseness” advantage. And obviously ising
auto as a kludge for a type of polynorphism where you switch around header
files, etc., to arrange the upstream type to change and rely on other code’s
use of auto to “just work” is a severe and critically flawed kind of anti-
pattern.

In most cases it won’t be anywhere near this easy anyway, the the type of the
function parameter should already have had an application-specific typedef to
understand its meaning, so the extra use in places where auto is used would
amortize it further.

And since you get much more benefit from the typedef way in more complex
situations, it would be better style to just use it consistently by using it
in simpler situationd too, especially since when the situation is simple, like
this example, auto by definition doesn’t provide an advantage.

This is why I specifically used the word “strictly” like the mathematical
sense of strictly increasing or a dominated function. The typedef approach for
this is superior _unilaterally_.

~~~
xyrouter
> In most cases it won’t be anywhere near this easy anyway

Then don't use auto in such cases -- is the point of the discussion here.

Use auto anywhere it is indeed nearly this easy.

When the situation is simple, auto clearly provides an advantage of
automatically deducing types that are very obvious to humans and the compiler.
The argument here is that not having _explicit_ types here is better.

Your cannot use the word "strictly" here in the mathematical sense at all
since you have not mathematically established that explicit types for simple
cases are better. It is only your subjective opinion that explicit types are
better even in simple cases and such an opinion is far from the exactness and
axiomatic nature of mathematics.

You pretend as if the proposition that "explicit types are better" is an
axiom. It is not. To many of us such proposition is neither self-evident nor
obvious because many of us believe removing explicit types for simple cases is
better. As such, the usage of "strictly" here is merely a weasel word.

~~~
mlthoughts2018
> “When the situation is simple, auto clearly provides an advantage of
> automatically deducing types that are very obvious to humans and the
> compiler. The argument here is that not having explicit types here is
> better”

That’s not an argument for anything. It amounts to just saying “worse is
better” because _you_ have a preference for how it looks with auto, even
though being explicit with the type information is _not about you._

There’s absolutely no sense in which _less explicit_ information is somehow
better for code readers. The only reason you might choose _to pay the cost_ of
having less explicit type info is if it gives you some other benefit, like
shortening syntax.

But since typedef/typename offer that too, without the loss of being explicit,
it just means auto would be a suboptimal choice _even in the shorter /simpler
case too_.

~~~
xyrouter
> There’s absolutely no sense in which less explicit information is somehow
> better for code readers.

This is patently false. It has been demonstrated more than once in this thread
that there clearly are cases where the types can be deduced effortlessly and
avoiding explicit information leads to cleaner code.

Your claim is only your subjective opinion/belief and definitely not a fact.

We might just have to agree to disagree, but I would disagree with your claim
here in the strongest possible terms. Constantly pretending as if your
subjective beliefs comes off as obtuse and is in poor taste. There is one size
fits all standard for coding. You are free to follow your coding standards and
so is everyone else. What looks like an obvious argument to you may appear
absurd to another. Like I said, the world of software development is far more
heterogenous that you seem to believe it is.

~~~
mlthoughts2018
> “It has been demonstrated several times in this thread that there clearly
> are cases where the types can be deduced effortlessly and avoiding explicit
> information leads to cleaner code.”

I do not see a single example in this thread that demonstrates that. Absence
of explicit type info is an inherently bad thing. It’s never good to offer
code readers less explicit / more ambiguous info.

Like I said, you’d expect some offsetting benefit (what you call “cleaner
code”) to compensate _for paying the penalty of giving up valuable explicit
info_. But auto does not actually provide that in comparison to
typedef/typename. auto, by comparison, is not meaningfully cleaner code. It
_only_ loses valuable explicit information, without an offsetting benefit that
couldn’t otherwise be obtained without losing that valuable explicit
information.

~~~
xyrouter
> Absence of explicit type info is an inherently bad thing.

Let me reiterate: It is your opinion which many people here do not agree with
in the context of types obvious from context. Your opinion is definitely not a
popular coding standard and definitely not a fact. Most of the coding
standards are in favor of using auto for simple code where the type is
immediately obvious from the context.

The auto keyword is available to help people who do not share your opinion.
The auto keyword is present for teams which agree that absence of explicit
type info is a good thing for simple code with obvious types (obvious from the
context).

Teams that believe that absence of explicit type info is an inherently bad
thing don't have to use the auto keyword. But that belief does not become a
fact no matter how many times you reiterate that belief.

> paying the penalty of giving up valuable explicit info

That's your opinion. My opinion which many coding standards agree with: There
is no penalty in giving up explicit type info in a local loop variable. The
typo info is not valuable in that case.

~~~
mlthoughts2018
In my comments my goal was to describe why those other beliefs about auto are
just fundamentally mistaken. To lobby for a certain way to think of it.

My perspective on this actually is a very popular coding standard in some
communities. For example, in The Zen of Python [0] (which is an official
Python PEP) there is, “Explicit is better than implicit” right in the second
line. It’s quite a widely regarded principle in programming.

Still, given that I only lobbied to persuade that my perspective is the right
one in this case, and did not ever try to claim that people cannot have a
different opinion, your comment seems unrelated to earlier part of the
discussion.

[0]: <
[https://www.python.org/dev/peps/pep-0020/](https://www.python.org/dev/peps/pep-0020/)
>.

