
Near-duplicate features of C++ (2017) - nayuki
https://www.nayuki.io/page/near-duplicate-features-of-cplusplus
======
earenndil
Some of these absolutely makes sense and are justified; others...are actually
sane to have in c++. For instance:

> int foo(void)/int foo()

In c, int foo() means that foo takes any number of args; c++ rightfully
realised that that was stupid and removed that feature, which means int foo()
can now be used to mean foo takes no args. Also, int foo(void) is illegal in
c++.

Varargs in c are a complete pita to use, not to mention they literally do
something different from function overloading/default args.

Constexpr is much nicer and safer to use than a macro.

Using malloc and calloc to make class instances means you can have
unconstructed objects, which is a Bad Thing, and while introducing a new
operator to do that is not necessarily the _best_ way to handle that, it's a
perfectly valid way of doing so.

Libc random is just garbage, and libc++ random is actually decent.

setjmp/longjmp are more complicated to use than try/catch, and don't have all
the same functionality.

<iostream> functions are type aware (although really they should have used
fmt).

I'm no c++ apologist (it's an awful language), but this listicle is just...not
great.

~~~
kllrnohj
I think the problem is mostly in the tone of the opening paragraph and less in
the list itself. By prefixing this with

"Don’t automatically assume that C++’s situation is desirable, necessary, or
inevitable."

It seems to be trying to frame this as C++ having bad duplication. But many of
the things listed are not actually strict duplicates at all - like pointers
vs. references, since references cannot be null (yeah yeah you can force it
but 'int& foo = nullptr;' doesn't compile, either). Which if you then read the
explanations at the bottom tend to cover in a fairly non-biased way.

~~~
skookumchuck
> references cannot be null
    
    
        void test() {
          int* p = 0;
          int& r = *p;
        }
    

compiles without complaint.

~~~
hermitdev
I've gotten into arguments about this in the past.

As you've shown, references _can_ be null, but theyre not supposed to be and
are assumed nearly universally to not be.

The argument comes down to when the undefined behavior occurs: is it at the
deference to create the reference, or is it on the first memory access using
the reference? The language pedants will say the former, but in practice, it's
the latter.

In practice, you'll likely be able to invoke a member function on a null
pointer or reference, as long as that member doesn't directly or indirectly
access data members, or virtual functions of the type. Obligatory, I dont
recommend doing this or relying upon this behavior, it's just behavior I've
seen in my 2 decades of debugging C++.

~~~
kllrnohj
It's mostly important in the context of codifying nullability. If a function
returns a reference it's part of the contract that it doesn't return null.
Similarly, if a parameter is a reference it's part of the contract that you
can't pass it null.

It doesn't mean a method that takes or returns a pointer must allow null as a
valid value, of course, Optional<> is better for that. But standards-enforced
non-null is a very practically useful aspect of references that differ them
from pointers.

------
nitwit005
The tone of this is a bit silly. The main reason C++ was a success was due to
the backward compatibility with C. That backward compatibility has (fairly
obvious) downsides.

Objective-C has the same issue, for the same reasons.

------
tzhenghao
Well, one might grok C++ with the same perspective the author had, but I think
C++, and especially C++17, make the language much safer and friendlier for
beginners. C++17 for example is a big enough departure from pre C++11 days. If
you stick to the bleeding edge, it doesn't look anything close to C at all.

Author has a point there, but maybe, just maybe, we won't see "new"s or
"delete"s in most future codebases? (we have smart pointers now) Or non-
intuitive macros (yay constexprs!)

I see these as stepping stones from C to where we are today without breaking
everything at once or doing it with a whole other programming language.

~~~
otabdeveloper2
> maybe, just maybe, we won't see "new"s or "delete"s in most future
> codebases?

I haven't seen 'new' or 'delete' in the wild for a decade now. (Well, except
for various legacy libraries or student code on Github.)

~~~
roel_v
Me too. I've been writing pretty much only C++ for 20 years, and if I'd go
back and count, I think I'd have maybe a dozen times where I used new/delete,
none of those in the last 10 years. Like all languages that are actually used,
there is much one can complain about in C++, but claiming that one needs a lot
of manual memory management is done mostly by people who haven't written much
if any C++.

~~~
emsy
Or people who care about heap fragmentation and cache misses.

~~~
roel_v
Sure, but those people don't want GC either. Not to say that all languages
that are not C++ are garbage collected, but the people who complain about
manual memory mgmt are not the ones who care about those things.

------
prewett
The author missed an opportunity on the heap allocation section. Now you
aren't even supposed to do `new`, but rather std::shared_ptr<...> or
std::unique_ptr<...> (as appropriate). Except that you generally create them
with std::make_shared() and std::make_unique(), so that adds another 4 items
there.

And then you have the fun of passing those to a function. Do you pass by value
(shared_ptr) or by reference (I believe you do not) or pass a naked pointer
(when you need to pass a unique_ptr to a function that uses, but does not
modify or cache the pointer, if I remember correctly). And then suppose you
want to give away your unique_ptr, that's fun.

And if you want to deallocate your shared_ptr that's part of a cycle, you'll
need to either use a weak pointer or manually reset it.

~~~
geezerjay
> Now you aren't even supposed to do `new`, but rather std::shared_ptr<...> or
> std::unique_ptr<...> (as appropriate).

Apples and oranges. Smart pointers are wrappers over raw pointers. They extend
functionality, but are not the same.

> Except that you generally create them with std::make_shared() and
> std::make_unique()

Again, apples and oranges. You're confusing factory functions that wrap memory
allocation for convenience with actual memory allocation.

At most you could only complain that those factory methods duplicate the role
of constructors, which would be silly.

> And then you have the fun of passing those to a function. Do you pass by
> value (shared_ptr) or by reference (I believe you do not) or pass a naked
> pointer (when you need to pass a unique_ptr to a function that uses, but
> does not modify or cache the pointer, if I remember correctly).

There is no duplication in your examples. You are just unaware of how those
functionalities are used.

------
hellofunk
C++ is a more type safe language than C, and some of these “duplicated“
features are actually adding safety to the C versions. Most of these
“duplications“ are quite in line with the C++ philosophy and are there for a
good reason, reducing countless bugs and making code safer.

And comparing a function pointer to a lambda expression is quite laughable.

------
mhh__
Duplicate features, and thank god they exist: References, for example, have
probably stopped millions of pointer arithmetic bugs of the years.

Also, this should be viewed as C++ features C doesn't have: It's 2019 C,
shouldn't be accepted anymore

~~~
syockit
Reference is among one the things I really want to be included in C. Does it
break anything if it's introduced to the standard?

On the other hand, I still prefer C to C++ mainly due to the compile time (an
incremental compile in a typical large C project I deal with takes a second or
so, while C++ takes 3-4 seconds). C++ doesn't have to be slow, as long as you
use the needed features. But idiomatic C++ means you'd be using STL, class
inheritance, virtuals, operator overloading, templates and constexpr and
stuff, all of which contribute to longer compile times.

~~~
mhh__
I'd prefer longer compile times over runtime issues: Smart pointers are cheap,
and easy to use now. C++ compile times are largely due to the absolutely
ridiculous import system C++ forces you to use. Precompiled headers can
eliminate a huge amount of C++ compilation time.

The obvious solution is to avoid C++ when making new codebase, but that's
easier said than done.

------
xhgdvjky
Herb Sutter has said again and again that his goal is to create a language
that allows you to write less even though the committee is adding stuff. this
fits with their goal of backwards compatibility. I don't see an issue

------
Ace17
The intent of the author is not very clear.

Of course there are duplicate features, that's an obvious downside to keeping
backward compatibility while still allowing improvement.

If two equivalent/overlapping features were introduced in the same C++
version, then, yes it would be an indicator of a faulty design process.

------
nayuki
Thanks to everyone for the feedback. Here are my aggregated responses:

• I feel that many commenters missed the fact that I was writing about NEAR-
duplicate features of C++; I never said they were identical. But these
features overlap so much that often all the alternatives are applicable in a
real-world situation, and the choice becomes somewhat arbitrary, and you tend
to see divergence between different coders and over the years.

• I'm surprised to see that no one said anything like "Indeed, I do see these
near-duplicate features in the C++ codebase that I work with. And it bothers
me because I had to learn two things instead of one, and we have no stylistic
consistency".

• Some people expressed doubts about why I wrote the article. My intent and
tone are clearly exhibited in the opening three paragraphs of the article;
please reread them fully before judging.

• Many people dwelled on the difference between references and pointers. I
firmly support references and use them in my published code. But I've seen
many people who learn C++ after C, and they try to use C-style pointers for as
long as possible to avoid learning the new, native C++ feature which is much
safer. Hence you cannot avoid seeing pointers in C++ if you read/edit code
that come from diverse sources.

• Many of my bullet points received no comments. I take it that either no one
cares, or no one recognizes these points. For example, is it really sane for a
language to have 3 ways of initializing a variable? (int i = 0; int j(1); int
k{2};)

• A subtread about const-by-default is something I agree with, though the
syntax would mismatch semantics in C. Regardless, Rust is a relatively new
language with nearly all of C++'s powerful features, and indeed it implements
immutable-by-default.

• I used new/delete in C++ in my early days, as a naive coder coming from C
and Java with their manual management of memory allocation. But now I avoid
those and rely on std::vector, std::unique_ptr, and RAII, which are safer and
more concise.

• (Previous thread:
[https://www.reddit.com/r/cpp/comments/6clwkq/nearduplicate_f...](https://www.reddit.com/r/cpp/comments/6clwkq/nearduplicate_features_of_c/)
)

------
de_watcher
From the definition of the backward compatibility you get "old features still
work while the new features that do the same thing work too".

------
2bitencryption
Say what you will about c++, but it's the only language I know where you can
declare classes as "friends" of one another.

~~~
hellofunk
It’s there for very rare purposes, and it’s considered a code smell if you
ever use that feature.

------
starchild_3001
Updates/upgrades are justified. Using old style in new code, often, isn't.

