
My Most Important C++ Aha Moments (2006) - adamnemecek
http://www.artima.com/cppsource/top_cpp_aha_moments.html
======
humanrebar
> Visitor lets you define a new operation without changing the classes of the
> elements on which it operates.

Yes, but that's not the real point. You can do that in other ways. Creating a
new std::algorithm does that, for example.

The real purpose is letting one function be polymorphic in multiple
directions. This is called multiple dispatch. Some languages support this as a
built-in feature, but C++ doesn't, so a couple different design patterns were
invented to fill in this gap. The Visitor pattern is the most popular and is
probably the easiest to work with.

[https://en.wikipedia.org/wiki/Multiple_dispatch](https://en.wikipedia.org/wiki/Multiple_dispatch)

That is, it lets you do this:

    
    
        polymorphicOnThis->doSomething(alsoPolymorphicOnThis);
    

That is, for each permutation of the two types (polymorphicOnThis and
alsoPolymorphicOnThis), you can define new specific behavior.

~~~
munificent
You _can_ use visitor to implement multiple dispatch, but that's not it's
"real" point. The real point, as stated, is to allow polymorphic methods that
aren't defined directly on the class.

I've seen a lot of implementations of the visitor pattern, and almost all of
them used it for single dispatch.

~~~
kazinator
You are right.

The Visitor Pattern doesn't _provide_ multiple dispatch.

The Visitor Pattern _relies_ on multiple dispatch, and so it provides a form
of it _to itself_.

The Visitor Pattern doesn't go away in a language with multiple dispatch; its
boiler plate code is just reduced.

What the Visitor Pattern is about is to bring together the benefits that arise
from a cluster of ideas: first class functions, and generic functions.

In the Common Lisp object system, OOP functions are "generic functions" which
stand alone and are not tied to classes. If we have a "frobosity" method and a
list of objects, we can map that list easily to get every object's frobosity:
(mapcar 'frobosity objlist). That is to say, we have visited each object in
objlist. Each object has effectively "accepted" a "visit" from the frobosity
function.

Now what we might want is instead of using generic function, to use a
"funcallable object". let's use mapc instead of mapcar, since we don't need to
collect the results into a list. The visitor-object itself can do something
useful along those lines:

    
    
      (mapc visitor-object objlist)
    

This object carries state and can do useful things, like inquire each object
about its frobosity and add them together.

since we don't have funcallable objects standard Lisp we can borrow the
dispatch kernel of the Visitor Pattern:

    
    
      (mapc (lambda (list-obj) (accept visitor-object list-obj)) objlist)
    

I.e. we use a method called accept and then curry it with a lambda to simulate
visitor-object being funcallable. This lambda is just _using_ double dispatch,
just like the Visitor Pattern _uses_ its two-dispatch simulation of double
dispatch.

If we have that real double dispatch, it's just more convenient to write the
rest of this cruft. It's just a simple, linear bunch of definitions like:

    
    
      (defmethod accept ((visitor this-class) (obj that-class))
         ;; handle this-class + that-class combination
        )
    

The double dispatch itself isn't the point of the pattern. The point is to use
an object as a function which is mapped over some other objects, and that
object does something useful: accumulates some information, pretty-prints,
translates the structure to something else, etc.

------
humanrebar
> Realizing that C++’s “special” member functions may be declared private

Don't do this anymore! Just delete them:

    
    
        MyClass& operator=(const MyClass&) = delete;
    

Private-really-means-deleted was a cool and very useful trick before C++11. So
useful, that it was a shame to require secret knowledge and aha moments to
use. So they just made it a normal feature of the language. There's also a way
to explicitly use the default implementation:

    
    
        MyClass& operator=(const MyClass&) = default;

~~~
kazinator
Alternative view: if you must use C++, stick to nothing later than C++2003.

Anyway, declaring a constructor " = delete" isn't the same thing as making it
private. If it's private, the code in your class scope can still use it. If
you can't trust your class private code to do things correctly, then you're
screwed; go join IT and write backup shell scripts. So basically this "=
delete" is just another ear growing out of the elbow of C++.

~~~
prodigal_erik
The reason for testing and static typing is that you can't trust yourself to
be perfect. I want to declare that I won't do things that don't make sense, so
the tools will tell me if I do them unintentionally.

~~~
kazinator
In 1998 C++, we can solve the problem of how to make a class non-copyable,
even to code that is in its class scope.

We can create a dummy class which has a private copy constructor that is not
implemented:

    
    
      class noncopyable {
      private:
        noncopyable(const noncopyable &);
      };
    

then simply make this a data member of a class that you don't wish to be
copyable:

    
    
      class whatever {
      private:
        noncopyable nc;
      };
    

The default copy constructor generated by the C++ compiler performs a member-
for-member copy, which involves copying nc. That is not possible, so in effect
that copy constructor is defeated, not unlike by "= delete".

Alternatively use inheritance to mix this in as a trait:

    
    
      class whatever : private noncopyable {
        // ...
      };

------
stupang
I love C and Objective-C. I can't stand C++.

Is that common among programmers?

~~~
hellofunk
It's easy to dislike or hate C++ due to its immense complexity, and
programmers like to be able to keep simple abstractions in their head so they
can concentrate their energies on their problem domain without worrying about
language confusion. With C and many other languages, this is possible as they
are smaller languages. C++ on the other hand: is there any language with
widespread use that is so deep and complex? However, there are undeniable
conveniences to C++ (safety, a good library, much more -- especially
functional-style programming with lambdas now) that can speed up your work
process, and if you can accept that you will never know the entire language
and can just use those bits that make things easier, it is possible to have a
healthy relationship with the language. And it offers tremendous room to grow
as you get more and more comfortable with it. Few languages allow you to just
keep diving in deeper and deeper as you desire, while still letting you get to
work right away without being a master. Even template metaprogramming alone is
gigantic area of ongoing research -- the stuff you can do with it is mind-
bending and unavailable in nearly all other languages. Templates are, after
all, one of very very few wholly functional, immutable languages out there.
And template instantiation is the underlying force behind many of the modern
features like lambdas that allow you to access that power without actually
knowing templates well. It's a powerful language for which you must accept its
character flaws in return for its abilities.

It's doesn't hurt either that the libraries available in C++ are really cool.

~~~
lisper
> safety

Um, no. C++ can be safer than C (which is an incredibly low bar) but only if
you carefully constrain yourself to certain subsets of the language, which is
why the web is chock-a-block with byzantine "C++ coding standards" documents
describing all the rules you have to follow in order to avoid shooting
yourself in the foot.

~~~
kazinator
Indeed that not only you have to follow, everyone else on the project has to
follow, as well as every newcomer and every piece of third-party code that is
brought in.

"Avoid the parts you don't like" works best for one programmer working alone.

~~~
Retra
I had an instructor once that insisted on having a single exit point for all
loops. "Never use break or continue or return from a loop." The argument was
that understanding control flow is more complex when you don't have to deal
with these things.

However, those things exist in the language. You can't just pretend nobody is
going to use them, because there might be very good reasons to use them,
especially in an imperative context.

(Though I suppose if you're grading 100 projects, such a rule might make it
easier to get the code structured the same everywhere.)

~~~
kazinator
In the world outside of that instructor's classroom, there are coding
conventions which forbid certain things, with excellent reasons for doing so.

Back when ISO C++ was still staffed by people with useful ideas, they invented
various more specialized, safer casts that refuse to do "off topic"
conversions. These replace the "(type) expr" C casting notation.

A C++ coding convention document can cheerfully forbid C style casts; there
isn't any reason to use them.

------
saynsedit
Yep the beauty (and maybe curse) of C++ iterators is that they are a faithful
abstraction of pointers.

~~~
aksx
some people would argue that pointers themselves are a type of iterator

~~~
TheSoftwareGuy
I would argu those people are terribly wrong, as pointers serve more purposes
than iteration

------
hota_mazi
My most important C++ Aha moment was in 1995 when I wrote my first lines of
Java and realized that it was possible to write OO code without having to
fight the compiler every step of the way.

~~~
digler999
My Aha moment was in 2008 when I saw a java program consume 400+mb of RAM for
a _text editor_. Then if you clicked on too many things, it crashed under its
own weight (eclipse).

"But it has _garbage collection_ " they said. "You dont have to worry about
pesky _memory managment_ , the language has an _intelligent VM_ that handles
it _for you_!".

------
kazinator
That was 2006. Myer's most impressive "Aha moment" w.r.t. C++ came last
December, eclipsing the others:

[http://scottmeyers.blogspot.ca/2015/12/good-to-
go.html](http://scottmeyers.blogspot.ca/2015/12/good-to-go.html)

