
Perfect forwarding and universal references in C++ - luu
http://eli.thegreenplace.net/2014/perfect-forwarding-and-universal-references-in-c
======
ridiculous_fish
I find this stuff frustrating because it's so hard to figure out if I'm
implementing it properly. I can't rely on trial and error, or compiler
verification, because errors manifest as extra copying or other
inefficiencies, instead of observably wrong behavior.

In fact it's often hard to even figure out what's going on at all. For
example, in the MyKlass case.

    
    
        v.push_back(MyKlass(2, 3.14f));
    

What's being invoked: a move constructor or copy constructor? How would you
verify? You could maybe add a move or copy constructor with some logging, but
defining either of those can change which one gets invoked! So I guess you
have to hunt through the assembly.

Here's another case that terrifies me. Testing with clang 3.5:

    
    
        return a; // invokes a's move constructor
        return true ? a : a; // invokes a's copy constructor
        if (true) return a; else return a; // elides constructors using RVO
    

So even when you do get it right, seemingly irrelevant changes can silently
break it. It's so fragile!

How do other C++ programmers verify that they're getting this stuff right?

~~~
maxs
These days I am writing a lot of C++11 code. I find that I write two different
kinds of objects:

1\. copyable objects like

    
    
        class RGB { int r, g, b };
    

2\. big complex things that should never copy but can move

    
    
        class Foo { Foo(const Foo&) = delete; };
    

In case of (1), I don't care if move or copy constructors are used and I am
happy that the compiler will decide. In case of (2), if by accident I invoke a
copy constructor, I end up getting a compile-time error.

------
mpu
I am not sure what C++ brings in as a language.

If you want to get an idea how your code will work or even if it going to work
at all you have to first understand type deduction rules which are seemingly
completely unprincipled. When I compare the "rules" of C++ type deduction to
functional languages' type inference algorithms I can easily understand how
the latter work but I have no clue about what justifies the former.

Yet, in C++, if I want auto type inference on my recursive factorial function
I have to write "return 1;" syntactically before (i.e. where the order is the
relative position of _bytes fed to the compiler_ ) "return n*fact(n-1);",
otherwise the compiler is unable to find the type. I just don't understand.
The type deduction rules (say reference collapsing) are rocket science and
take a full blog post as their justification, but basic unification algorithms
known in the PL community for literally decades cannot be implemented in the
standard?

This language seems plain crazy, this kind of article makes me wonder why
people think it can be a reasonable choice for any kind of software project.

At least, C can fit in a brain.

~~~
pjmlp
> I am not sure what C++ brings in as a language.

C speed of execution, while offering support for large scale programming, OOP,
functional, generic and plain old imperative programming.

Ability to treat user defined types as first class citizens.

Standard library that offers all mechanisms to avoid the traditional C
pitfalls in terms of memory/resource leaks and out-of-bounds errors.

> This language seems plain crazy, this kind of article makes me wonder why
> people think it can be a reasonable choice for any kind of software project.

Except for Ada, very few languages offer similar capabilities and Pascal like
syntax is out of fashion.

> At least, C can fit in a brain.

Can you master all compiler specific behaviours not covered by the standard?!?

~~~
mpu
> Can you master all compiler specific behaviours not covered by the
> standard?!?

No, and the good part is: I don't need to :). All my programs are standard C11
(- concurrency).

~~~
yoklov
I think his point was that the standard (even C11) leaves a great deal of
implementation-specific behavior.

C++11 has the same problem (as it has the same implementation specific
behavior as C), but the standard library allows you to avoid most of it.

------
splinterofchaos
Not a bad article, but I'm so sick of the term "universal references". Perhaps
useful for explaining things to the uninitiate, but a more technical
understanding requires the knowledge that rvalue references can be made from
any l- or rvalue.

So when I see "Don't let T&& fool you here - T is not an rvalue reference" \--
yes it fricking is! And if you're going to work on a low enough type-level
that you need perfect forwarding, you NEED TO KNOW THAT.

~~~
wmkn
At C++Now 2014 there was some agreement on renaming 'universal references' to
'forwarding references'. Apparently, Scott Meyers, the guy that came up with
the term universal references, even agreed to update his upcoming book with
the new term.

~~~
splinterofchaos
At least "forwarding" specifies "for use in perfect forwarding", but it must
be made clear that this term refers to the syntactic phenomena and they are
not distinct from rvalue references.

------
amelius
C++ is getting more complicated and hence less interesting every day.

How is Rust solving this particular issue?

~~~
harry8
The fact they won't deprecate anything is appalling. There are warts on warts
on warts for sure and just about any criticism of C++ has some basis.

It may be surprising for you to learn that from the perspective of a
programming something C++11 is simpler and more straightforward than c++ was
previously. Believe it or not. I know didn't think it would be.

~~~
mmaldacker
They do deprecate things, e.g. exception specification and the auto keywords
was re-purposed.

