
Obscure C++ Features - DmitryNovikov
http://madebyevan.com/obscure-cpp-features/?viksra
======
voyou
Are these really that obscure? Well, the 3[array] thing is unexpected and also
not something you would use in real code, so it reasonably counts as obscure
(although it's such a hoary example of "obscure C" that it's almost familiar).
But pointers-to-members, template-template parameters, and the overloads of ++
and -- are, I would have thought, just standard features you would learn in
the normal course of learning the language.

~~~
10098
Yeah, out of all these only the "most vexing parse" struck me as somewhat
obscure simply because it's so insidious. The rest... not really that obscure.
I mean, come on. a[3] equivalent of *(a+3)? That's just pointer arithmetic.
Most of the other stuff is probably intermediate level.

~~~
Jare
Most people understand that a[3] and *(a + 3) yield the same result, not that
they are actually identical in terms of language syntax.

~~~
jheriko
they really aren't identical in the general case - this is a mistake on the
part of the author. trivially consider the case where i overload operator[]...

~~~
CJefferson
Yes, this is more of an obscure C feature (they are exactly the same in all
cases in C, the standard defines a[b] as *(a+b)), which is inherited by C++.

------
wyager
Every time I read about obscure C++ features, the more I'm convinced that the
language needs to be torn down and rebuilt from scratch.

~~~
btmorex
Whenever I read something like this, I'm reminded of that Winston Churchill
quote "It has been said that democracy is the worst form of government except
all the others that have been tried".

I think C++ usage is one of those things that you don't truly understand until
you've decided to start a new project in C++ even in 2013. For a reasonably
common feature set in real project (i.e. not a side project for fun), C++ is
the only option.

Specifically most alternatives exhibit one or more of the following:

* automatically managed memory (enough said)

* not expressive enough (I would throw C in this category even though C is a fine choice and often times the best choice for many projects)

* not nearly mature enough in terms of compiler/toolset/language in general (often, it's unclear whether the language will ever achieve that necessary maturity)

* not fast enough, usually for one of the above reasons, but sometimes for other reasons

~~~
reikonomusha
Automatically managed memory is almost always a _good_ thing. Aside from
preventing a huge class of bugs, it also is often more performant. Why
disparage it?

And I'd hardly label C++ as the epitome of expressiveness. A broad, haphazard
feature set doesn't necessarily imply it's expressive.

~~~
10098
I think he's talking about garbage collection, which is often perceived as
slow.

But you can certainly have automatic memory management in C++ as well. Stack-
allocated objects manage themselves, for dynamically allocated objects one can
use scoped/shared pointers. One added perk is that you can extend this
approach to deterministically manage not only memory, but any other resource
that can be acquired and released (e.g. locks). I actually like this approach
very much. The only problem I see there is that since you're still using raw
pointers, the runtime can't really move things around for efficiency since
that would invalidate the pointers. But in a garbage-collected language that
doesn't expose pointers, the heap can be defragmented during runtime, which is
nice.

~~~
pcwalton
> The only problem I see there is that since you're still using raw pointers,
> the runtime can't really move things around for efficiency since that would
> invalidate the pointers.

The biggest downside is not really efficiency but that you lose the memory
safety of a managed language doing this.

~~~
10098
True, but most of these issues can be mitigated by using best practices - use
vectors instead of raw arrays, avoid shared ownership (and use shared pointers
when you can't), etc. Of course, these don't make the language safe, but they
do help alleviate the problem a great deal.

I wonder if it's possible to design a language that would be able to prevent
these problems at compile time without incurring the runtime cost of managed
languages. I mean, we already can detect some problems using static code
checking tools (i.e. PVS-studio), so it must be feasible.

~~~
pcwalton
> I wonder if it's possible to design a language that would be able to prevent
> these problems at compile time without incurring the runtime cost of managed
> languages.

Rust does this. :) It is not easy to get right—the borrow checker required a
lot of design work to achieve the right balance between expressiveness and
practicality—but I think we've shown that it is possible.

(Disclaimer: I work on Rust.)

------
richo
C++11 is actually really, /really/ nice. If you haven't already you should
read up on it.

I was hating c++ for vague reasons I couldn't place. I was wrong.

~~~
72deluxe
I would agree. Although my work involves writing some code under VS2010 on
Windows so I can't use C++11 features, I still read Stroustrup's book. The
tour of C++ is particularly helpful as it covers many of the new features, and
offers tips like making the compiler work for you.

Stroustrup wrote an interesting paper (wish I could find it) in IEEE/Computer
magazine that strongly advocated static code and not overuse of dynamic_casts
etc. for speedy programs - this is making the compiler do all of the
optimization instead of doing everything/making many decisions at run time.

The Wikipedia article on C++11 covers many of the new features quite well, but
Stroustrup's recent revision of the C++ book is a joy to read - the fonts
help, as the previous one had some pretty horrible fonts in my opinion.

~~~
koyote
VS2010 actually does implement quite a few of C++11's features, you have to be
careful though and better read the MSDN documentation for each of them.

A weird case I found recently was that the VS2010 implementation of
std::to_string(...) only supports long long, unsigned long long and long
double. [http://msdn.microsoft.com/en-
us/library/ee404875(v=vs.100).a...](http://msdn.microsoft.com/en-
us/library/ee404875\(v=vs.100\).aspx) Not very useful for most cases. Luckily
VS2012+ supports the rest of the number types.

------
taspeotis
This one [1] surprised me.

 _Q1: Is the following code legal C++?_

    
    
        string f() { return "abc"; }
    
        void g() {
            const string& s = f();
            cout << s << endl;    // can we still use the "temporary" object?
        }
    

[1] [http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-
the...](http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-
important-const/)

~~~
mhogomchungu
I do not think they had this kind of usage in mind when they allowed it and i
think the use case for it is in using a const reference to a function
argument.Most people use this all the time without thinking about it or
wondering what is the origin of the variable they are accessing through the
const reference.

eg is when you have a function like below:

    
    
      void foo( const string& str )
      {
             cout << str ;
      }
    

The above function takes a const reference to string and uses it without
caring if the passed in variable is a temporary variable or not.

Now declare a function as:

    
    
         string bar() { return "abc" ; }
    

and the above two function can be used with:

    
    
         foo( bar() ) ;
    

I think this use case is the one that allowed the use of temporary variable
from a const reference and people do this all the time.

~~~
MaulingMonkey
> I do not think they had this kind of usage in mind when they allowed it

I think they did based on 8.5.3 which has this example:

    
    
      struct A { };
      struct B : public A { } b;
      extern B f();
      const A& rca = f(); // Either bound to the A sub-object of the B rvalue,
      // or the entire B object is copied and the reference
      // is bound to the A sub-object of the copy
    

As well as 12.2 para 5:

    
    
      class C {
        // ...
      public:
        C();
        C(int);
        friend C operator+(const C&, const C&);
        ˜C();
      };
      C obj1;
      const C& cr = C(16)+C(23);
      C obj2;
    

In the C++ standard which talks about the temporary bound to cr existing for
the duration of the entire program? Moreover, why would a rule for const
references be specifically needed to handle foo( bar() );, but no equivalent
rule for bar().foo(); which handles an implicit _non-const pointer_?

------
ret
See also FQA:
[http://www.yosefk.com/c++fqa/index.html](http://www.yosefk.com/c++fqa/index.html)

------
kenrose
The function try blocks is new to me. It's actually kind of elegant (for C++).
Granted, the reason I've probably never encountered it in the wild is that
there are probably very few scenarios where you have a member in the
initializer list where you want to handle an exception on initialization. If
you were trying to handle an exception of an instance variable initializing,
most people would just move that member to the constructor body (e.g., if you
have a pointer). The only time this would be useful then is for members that
don't have default initializers (e.g., a reference).

~~~
jasonzemos
It's nice to avoid the default initialization and construct a member directly
with the argument you want. Then you can construct a second member which might
depend on that previous one, etc. This can even include lambdas right in the
initialization list, and all of this may throw and log an error with the catch
block. Personally, I rarely like to see code in the actual constructor body
anymore.

------
mcguire
" _The tokens and, and_eq, bitand, bitor, compl, not, not_eq, or, or_eq, xor,
xor_eq..._ "

Hey, I remember when those were introduced!

I was the sysadmin responsible for supporting the default gcc on all of a
computer science department's non-research machines and upgraded gcc between
semesters. I then got to spend a couple of days rewriting some grad students'
code because the had used "and" and "or" as variable names.[1]

Fun times!

[1] Many of whom were upset because the upgrade had changed the
preprocessor/compiler interface and broke their research compiler.

------
throwaway0094
"The tokens and, and_eq, bitand, bitor, compl, not, not_eq, or, or_eq, xor,
xor_eq, <%, %>, <:, and :> can be used instead of the symbols &&, &=, &, |, ~,
!, !=, ||, |=, ^, ^=, {, }, [, and ]."

In C, the first few are only if you include the C99 header <iso646.h> (the
later ones are just digraphs). Are they included by default in C++?

------
kalimatas
I knew about the first sentence, but the second really obscure.

    
    
        Accessing an element of an array via ptr[3] is actually just short for *(ptr + 3). 
        This can be equivalently written as *(3 + ptr) and therefore as 3[ptr], which turns out to be completely valid code.

~~~
Skinney
This was actually written in the book I used to learn C (I think, at least I
knew this early on). It seems completely irrational when someone tells you
that ptr[3] == 3[ptr], but with the above explination it makes perfect sence.

~~~
chmike
Yes and it also explains why array indexing starts at 0.

~~~
unfamiliar
Not really. It would be just as easy to say a[x]=*(a+x-1) and let array
indices start at 1.

~~~
valisystem
actually yes, [] derives from BCPL, see evincarofautumn comment just above. (
link to comment,
[https://news.ycombinator.com/item?id=6886488](https://news.ycombinator.com/item?id=6886488)
derived from this submission :
[https://news.ycombinator.com/item?id=6879478](https://news.ycombinator.com/item?id=6879478)
)

------
eonil
I couldn't even imagine something like ref-qualifier and function as template
parameter.

------
72deluxe
Most people have come across member function pointers - event handling! They
are (in some libraries) wrapped in macros for easier readability but most
beginners use function pointers without realising.

------
agoandanon
This article misses _WHY_ certain things are the case, and has several
incomplete or incorrectly-explained points.

~~~
BruceIV
Yeah, the one that got me is "branch on variable declaration", which I believe
is actually a special case of the fact that assignment is actually an
expression which returns the assigned value; `a = b = c = 42;` works for the
same reason.

------
andhow
I've always thought that this ranked among the most obscure:

    
    
      typedef int F(int);
      class C { F f; };
      int C::f(int i) { return i; }
    

I've never seen it used in practice...

------
jheriko
surprised that most of this is considered obscure... and also teh square
bracket thing is only for built-in types (e.g. pointer types) because it can
be overloaded...

the constructor vs. function choice is a classic gotcha for instance. i've had
to explain it to juniors many times.

