Learning C++11

Started by
44 comments, last by taby 7 years, 4 months ago

I'll eventually get to understand C++14, but for now I'm focusing on C++11.

One of the gems that I found in The C++ Programming Language, Fourth Edition is the range-for-statement (page 233 in the softcover edition, section 9.5.1), which trims down like 50 characters of code down to 10 characters.


 
#include <vector>
using std::vector;
 
#include <set>
using std::set;
 
#include <iostream>
using std::cout;
using std::endl;
 
int vecsum(const vector<int> &v)
{
    int s = 0;
    
    for(int x : v)
        s += x;
    
    return s;
}
 
int vecsum2(const vector<int> &v)
{
    int s = 0;
    
    for(vector<int>::const_iterator i = v.begin(); i != v.end(); i++)
        s += *i;
 
    return s;
}
 
int setsum(const set<int> &s)
{
    int sum = 0;
    
    for(int x : s)
        sum += x;
    
    return sum;
}
 
int setsum2(const set<int> &s)
{
    int sum = 0;
    
    for(set<int>::const_iterator i = s.begin(); i != s.end(); i++)
        sum += *i;
    
    return sum;
}
 
 
 
int main(void)
{
    vector<int> v;
    
    v.push_back(1);
    v.push_back(3);
    v.push_back(5);
    v.push_back(7);
    v.push_back(9);
    v.push_back(11);
    
    cout << vecsum(v) << endl;
    cout << vecsum2(v) << endl;
    
    set<int> s;
    
    s.insert(1);
    s.insert(3);
    s.insert(5);
    s.insert(7);
    s.insert(9);
    s.insert(11);
    
    cout << setsum(s) << endl;
    cout << setsum2(s) << endl;
    
    return 0;
}
 
 

Do you know of other gems?

Advertisement

vector::const_iterator

You might want to look into 'auto'

(removed; I misread your post)

How about this?


vector<int> v = { 1, 3, 5, 7, 9, 11 };

Are you actively using templates? If so, there is a lot of cool stuff, but if you are not writing template code then its pretty useless.

Move-semantics/operators are really useful (you really need to read them up in detail though), and theres lots of other good stuff regarding class constructors:


class Foo
{
	Foo(int, float)
	{
	}
}

class Bar :
    public Foo
{
	using Foo::Foo; // using constructor, now there is a Bar(int, float).
	
	Bar(int)
	{
	}

	Bar(float x) :
		Bar((int)x) // delegating constructors => calls Bar(int)
	{
	}
} 

Just stuff you can easily use in everyday-code, there is lots of specialized stuff like the template things I mentioned, lambas, std::bind etc...

How about this?


vector<int> v = { 1, 3, 5, 7, 9, 11 };

I get an error when I do that:

error: non-aggregate type 'vector<int>' cannot be initialized with an initializer list

vector<int> v = { 1, 3, 5, 7, 9, 11 };
I'm using g++ on macOS Sierra.

vector::const_iterator

You might want to look into 'auto'

Will do. Thank you. So far I know that auto in C++11 is not the same as auto in C (whatever version).

I found the following code using auto:


int vecsum3(const vector<int> &v)
{
    int s = 0;
    
    for(auto p = v.begin(); p != v.end(); p++)
        s += *p;
    
    return s;
}

It works like a vector<int>::const_iterator in this case I'd hope, automatically deducing the type. That's pretty cool too!

This also works:

int vecsum4(const vector<int> &v)
{
    int s = 0;
    
    for(auto p : v)
        s += p;
    
    return s;
}

I get an error when I do that:

Under VS2013+, this compiles, and the {}-initilization as used here should be perfectly legal C++11 code after all, so maybe its just something that your g++-version doesn't support it yet for some reason.

How about this?


vector<int> v = { 1, 3, 5, 7, 9, 11 };


I get an error when I do that:

error: non-aggregate type 'vector<int>' cannot be initialized with an initializer list

vector<int> v = { 1, 3, 5, 7, 9, 11 };

I'm using g++ on macOS Sierra.


My guess is you didn't include <vector> or you didn't make std::vector visible. Try this:

#include <iostream>
#include <vector>

std::vector<int> v = { 1, 3, 5, 7, 9, 11};

int main() {
  for (auto i : v)
    std::cout << i << '\n';
}

One other gem is the std::chrono high resolution timer in C++11 (page 123 in the fourth edition softcover):


auto t0 = high_resolution_clock::now();
 
size_t total = 0;

for(int i = 0; i < 100000000; i++)
    total += i;

auto t1 = high_resolution_clock::now();
    
auto ms = duration_cast<milliseconds>(t1 - t0).count();
    
cout << ms << endl;

The beautiful thing about std::chrono is that it works in Xcode 8.1, so you can use it to create millisecond timing on the iPod/iPad. Awesome.

Before std::chrono, one would need to use a different timer code on Windows and Linux. One must explicitly measure wall time in Linux, if I recall correctly.

This also works:


int vecsum4(const vector<int> &v)
{
    int s = 0;
    
    for(auto p : v)
        s += p;
    
    return s;
}

Note also that 'auto' in that case is an 'int' copying by value (which in the case of an int, is preferred).

But if you had something like, say, std::string, you'd probably rather have a reference - and usually you'd want it const, unless you are modifying it.


std::vector<std::string> myStrings = {"red", "blue", "green"};

for(const auto &str : myStrings}
{
    std::cout << str << std::endl;
}

Btw, while C++11 was a major addition, C++14 was a minor addition. C++17 will be another major addition, and C++20 (or whatever) will presumably be a small addition - this is the general 'tick-tock' upgrade plan they are going for.

The biggest new dealy with C++11 is move semantics (and the related 'rvalues'), which seem confusing (because people explain them poorly) but are actually really simple.

However, my favorite general syntax-related improvement is that you can now initialize class members directly in the class declaration:


class MyClass
{
    public:
    
    int myInt = 357;
    std::string myString = "Yay!";
};

Lambdas are also great! Functions (actually 'functors', because they are really classes in many cases) that you create inside other functions (among other things):


#include <iostream>

int main()
{
    int counter = 0;
    
    //-----------------------------------------------
    //vvv Lambda vvv
    
    auto doSomething = [&counter](const std::string &str)
    {
    	++counter;
    	
        std::cout << "Called 'doSomething' " << counter << " times. "
                  << "Calling with with '" << str << "' as a parameter." << std::endl;
    };
    
    //'doSomething' is a variable that *holds* an instance of the lambda to call later, like a function pointer.

    //-----------------------------------------------
    
    doSomething("Penguin");
    doSomething("Aardvark");
    
    for(const std::string &element : {"fire", "water", "earth", "wind"})
    {
        doSomething(element);
    }
    
    return 0;
}

[Test the code right in your browser]

(Note that the lambda using what's called a "capture" has a reference to another variable ('counter')).

The general syntax looks like this:


int myFunction(int arg) { } //Regular function
            [](int arg) { } //Lambda

auto myFunction(int arg) -> int { } //Another way of writing regular functions (specifying the return type after the arguments)
             [](int arg) -> int { } //Demonstrating the same thing with lambdas

Lambdas in other languages are typically called 'closures' or 'anonymous functions'.

This topic is closed to new replies.

Advertisement