You originally learned C++ piece-by-piece over time, so don't worry about not understanding C++11 all at once.
C++11 extends C++ in a few ways, but it doesn't remake the entire language, so all your prior C++ knowledge is still useful and entirely relevant.
Lambdas confuse me, in both their syntax and their purpose.
(You should know about function-pointers and callbacks before you learn lambdas)
Lambdas are functions. Lambdas are functions. Lambdas are functions.
(Okay, that's a lie. But it's one of those lies that make learning a subject easier to understand)
This is a normal function:
void MyFunction(int x) { x = 5; }
This is an unnamed lambda function:
[](int x) { x = 5; }
Ignore the "[]" for a moment. That is the symbol used to begin a lambda, but it serves another purpose as well.
Let's compare the two side-by-side:
void MyFunction(int x) { x = 5; }
[](int x) { x = 5; }
One's a function named 'MyFunction'. The other is unnamed.
Cool. By why? Normal functions can only be defined outside of any function. In C++, you can't define a function inside a function.
int main()
{
//----------------
//INVALID:
int foo()
{
return 0;
}
//----------------
int x = foo();
}
Lambdas can only be defined and used inside functions:
int main()
{
auto foo = []()
{
return 0;
}
int x = foo();
}
That declares an unnamed function that returns 0. Then 'foo' becomes, because of 'auto', a function-pointer or function-reference to the unnamed function.
But imagine I wanted to use a local variable of 'main()' in my lambda...
int main()
{
int localVar = 357;
auto foo = []()
{
return localVar;
}
int x = foo();
}
...lambdas can "capture" the local variable, either by value or by reference, and access it later (assuming the reference is still valid).
This is why lambdas technically aren't functions: they are actually
functors - which are normal C++ classes that behave like functions but can have member variables and other stuff. Functors is the name programmers give to the
idea of classes that behave like functions. Though not a built-in feature of C++, C++ is flexible enough to allow it, and functors have always been in C++ (functors are just classes).
So a lambda is, behind the scenes, an unnamed
class that pretends to be a function. Classes can pretend to be a function by overloading the operator '()', to be used like myClass(), and can accept parameters.
There's a new feature in C++11 for regular functions. Instead of specifying a normal C++ function like this:
int MyFunc() { return 0; }
You can optionally define it like this:
auto MyFunc() -> int { return 0; }
A bit weirder, but useful with templates in some cases.
Anyway, you use the same syntax if you want to specify the return type of a lambda:
[]() -> int { return 0; }
Now, I mentioned that lambdas can "capture", either by value or by reference, local variables.
You can specify how you want to capture them, by using the [] part of the lambda and inserting a = (for capture-by-value) or an & (for capture-by-reference).
You can also specify each variable that it captures manually, and decide whether they are caught by reference or value individually.
Rvalue / moving seems confusing just as well;
That's the thing that'll trip up most people coming to C++11, I think. Luckily, it's something that can be entirely ignored without any harm until you are ready to learn it.
I think the hardest part of learning move-semantics is that it's not explained very well. Once you understand it, you can shrug and say to yourself, "Hey, that isn't half as complex as I thought". And it doesn't require some weird twist of thought either, just a halfway decent explanation without scary terminology.
Imagine you had a class that holds alot of data by pointer:
class MyClass
{
HugeData *lotsOfData;
};
If you did this:
MyClass myClassA;
MyClass myClassB = myClassA;
The default copy-constructor of C++ would have only a bit-level copy of MyClass be made.
That means, the
pointer would get copied, but not the
data it points to.
'myClassB' and 'myClassA' would both point at the same data!
This means, if you have myClassA modify the data, myClassB's data (the same data) would get modified too. That'd be a bug in your code.
And when myClassA goes out of scope and destructs, it'd free the data, and the next time myClassB tries to access it, your program would crash.
Thankfully, C++ lets us specify our own copy functions. There's the copy-constructor function and the copy-assignment function.
Now, we can tell MyClass that when it is copied, it ought to manually copy the actual data and not just the pointers.
But copying is expensive. What if we were doing this:
MyClass myClassA = ...lots of data...;
MyClass myClassB = myClassA;
return myClassB;
We'd be
needlessly copying a huge amount of data, because we never modify or use myClassA again. In this kind of circumstance, we'd actually
prefer that myClassA just hands myClassB the pointer instead of copying the data, and that myClassA should know not to bother freeing the data, and let myClassB own and free the data on its own. We'd rather, in some circumstances, transfer mental "ownership" of the data and just copy a tiny pointer instead of copying a huge block of data. We call this "moving" instead of "copying".
So C++11 added a way to give classes custom move functions. Just as there are "copy constructors" and "copy assignment operators", C++11 added "move constructors" and "move assignment operators".
And that's it. So when we do this:
myClassB = myClassA;
Data is copied. The class's copy functions get called.
And when we do this:
myClassB = std::move(myClassA);
Data is moved. The class's move functions get called.
When a move function gets called, and myClassA moves its data to myClassB, it gives up its right to that data, so you shouldn't try to use myClassA anymore, because it doesn't have any right to that data and might not be able to access it. But you can assign something else to myClassA before continuing to use it.
There's more details, like when and where C++ moves data instead of copies it, but the above is the big picture "why" and "how". Move semantics gives official standardized support for an idea that already existed - transferring ownership of data when a copy isn't needed.
decltype... gah.
If you understand 'auto', 'decltype' is pretty similar.
int x = 5;
auto y = x; //'y' is an int
int x = 0;
decltype(x) y = 0; //'y' is an int
decltype(x) provides a way to say, "
the type that is 'x'".
decltype doesn't actually
use 'x', it just (at compile-time) checks what type 'x' happens to be.
Unlike 'auto' decltype doesn't have to create a variable - it can be used for things like typedefs:
typedef int MyTypeA; //Makes 'MyTypeA' another name for 'int'.
int x = 0;
typedef decltype(x) MyType; //Makes 'MyTypeB' another name for int, because decltype(x) means 'int' since 'x' is int.
MyTypeB y; //Uses the typedef 'MyTypeB', which is an int.
You wouldn't be able to do that with 'auto', because the compiler wouldn't know what type 'auto' was supposed to be:
typedef auto MyType; //Won't compile, because what type is 'MyType' here?
It is mostly used in templates, where you don't actually know the type of stuff until compile-time.
The only thing I've understood so far is auto, and to a half-way extent: the new for loop style.
I like the new for loop (called 'range-based for loop' when you want to get technical, or 'range-for' for short).
Given a container like std::vector, range-for will iterate over the entire container.
for(int x : vectorOfInts) //Iterate, copying each element into 'x'.
for(int &x : vectorOfInts) //Iterate, making 'x' reference each element.
for(const auto &x : vectorOfWhoKnowsWhat) //Iterate, making 'x' be a const-reference to each element.
It's a shorthand way of writing:
for(std::vector<int>::iterator it = std::begin(vectorOfInts); it != vectorOfInts); ++it)
{
int &x = *it;
}
...which isn't very pleasing to look at.
How is it some clicked onto the new concepts rather fast?
The same way you are. I started in late 2010 or early 2011 (C++11 was finished in late 2011, so I started before it was standardized).
Some people have been following C++11 for years and years - bits and pieces of C++11 have been studied and talked about since 2003, though its changed alot.
Also, if you are only just now getting a grasp of regular C++'s features, alot of C++11 is easier to understand if you know advanced C++ features and mindsets - which you pick up overtime.
And where can I go to get to that point?
Bit by bit, piece by piece, through use, through reading forum threads and asking questions and reading articles.
These videos helped me alot. I spent a good week watching the first Going Native's videos, and then invested a week watching the second Going Native.
Some of it went right over my head... but hey, when originally learning C++, alot of normal C++ went over my head the first time I encountered certain features. But after encountering a feature multiple times (like templates in C++98, or like move-semantics in C++11), is starts to sink in, bit by bit.