Sign in to follow this  
taby

Learning C++11

Recommended Posts

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?

Edited by sjhalayka

Share this post


Link to post
Share on other sites

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...

Edited by Juliean

Share this post


Link to post
Share on other sites

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.
Edited by sjhalayka

Share this post


Link to post
Share on other sites

 

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;
}
Edited by sjhalayka

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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';
}

Share this post


Link to post
Share on other sites

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.

Edited by sjhalayka

Share this post


Link to post
Share on other sites

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'.

Share this post


Link to post
Share on other sites

You may want to jump on one of the two major C++ reference sites and just dig around for a while and check out anything that looks interesting. Some of the new libraries (chrono, random, etc) aren't easy to pick up just from the member functions, but those sites will help you to generate a list of things to Google around for.

Share this post


Link to post
Share on other sites

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

 

[...]

 

Do you know of other gems?

 

I really like this blog post: The Biggest Changes in C++11 (and Why You Should Care). It is clear, concise, and makes a great starting point for further reading. Also, the same site has several other good posts on the subject.

 

And why are you using gcc instead of Clang on macOS? Just curious.

Share this post


Link to post
Share on other sites

I really like this blog post: The Biggest Changes in C++11 (and Why You Should Care). It is clear, concise, and makes a great starting point for further reading. Also, the same site has several other good posts on the subject.

 

And why are you using gcc instead of Clang on macOS? Just curious.

 

 

I'll try clang. Thank you.

Share this post


Link to post
Share on other sites

You might want to look into the std algorithms (include <algorithm> and <numeric>). That stuff can eliminate a lot of for-loops and it works on vectors sets, lists etc. all the same way. For your example it would look like this:

#include <vector>
#include <set>
#include <numeric>
#include <iostream>

int main()
{
    std::vector<int> v{1, 3, 5, 7, 11};

    // the vector sum using accumulate from <numeric> header.
    std::cout << std::accumulate(v.begin(), v.end(), 0) << std::endl;

    std::set<int> s{1, 3, 5, 7, 11};

    // same function for the set.
    std::cout << std::accumulate(s.begin(), s.end(), 0) << std::endl;

    return 0;
}

Share this post


Link to post
Share on other sites

Be careful with auto.  It is far to easy to abuse.

 

The language standard committee fought the automatic type deductions for years, and it wasn't until Lambda expressions were formalized that they finally allowed it in.  There are some lambda types that are unexpressable in code.  Effectively the type is "block of compiled source code", but it doesn't have a type name that can be stated.  I'm sure if it wasn't for lambdas it would still be on the suggestion list rather than in the standard.

 

Many inexperienced developers -- and sadly experienced developers who ought to know better -- assume that since the compiler can deduce the type, the compiler should deduce the type everywhere.

 

Overusing auto makes code harder to understand.  Sure the compiler can figure out what type it is, but the programmer may have no idea what actual type is being used and will need to guess.

 

It also can lead to subtle and non-subtle bugs. The auto type you thought was one thing is unexpectedly -- but correctly -- something else.  Maybe you thought it was a 32 bit integer but it turns out to be a 64 bit unsigned value. Such a bug may go undiscovered for quite some time, perhaps silently resulting in implicit type promotions, and it isn't until somebody gets a warning about signed/unsigned mismatch or whatever that someone finally notices.

Share this post


Link to post
Share on other sites

Sure the compiler can figure out what type it is, but the programmer may have no idea what actual type is being used and will need to guess.


Although I'd argue this is where good tooling comes in; VS2017RC when confronted with 'auto' types will, if you hover over the variable with the mouse, tell you what it is.

This is also an argument in favour of good variable naming, clear function naming and comments in that order.

Share this post


Link to post
Share on other sites

The mouseover thing has been the case since the inception of auto. Mousing over any variable in VS should show its fully deduced type.

 

I've heard some folks (names that you know) advocate using auto everywhere with abandon, which I think is ill advised because it does make the code harder to read in some cases and sometimes you want to specify a type explicitly. However, I do tend to use auto quite a lot these days, especially for returned values.

auto sound = CreateSound("something");
sound.play();
sound.rewind();
//This is okay with me
auto val = 1 + 1;
//this is just annoying - I don't want to have to think about what type that will be.

YMMV

Share this post


Link to post
Share on other sites
The mouseover thing has been the case since the inception of auto. Mousing over any variable in VS should show its fully deduced type.

 

Only in IDEs that support it.  

 

If the only way to know the true type of things is to play peek-a-boo with the variable as it hovers over the mouse which works only in editors that have access to the full source tree, that's a problem. What happens if you need to edit a file and you don't have all the source conveniently available so it is improperly deduced? What if you are editing code in a tool that doesn't support the feature?

 

There are plenty of times I will pull up Notepad++ or similar text editors to quickly edit source code, and if I run across a function that uses autos for nearly everything I am going to be far less productive than if I could see the actual type.

 

 

As for where to use it... For types that are difficult to put together or long to type, like iterators to containers with template parameters, that is generally a good reason.  Lambdas obviously require them in several situations.  But typing "auto" instead of "int" is worse than just lazy because it is an extra character.  It is a longer word AND it obfuscates the type.

Share this post


Link to post
Share on other sites

There are plenty of times I will pull up Notepad++ or similar text editors to quickly edit source code, and if I run across a function that uses autos for nearly everything I am going to be far less productive than if I could see the actual type.


I feel like this is generally true for anything, not just auto. IDEs are a force multiplier for productivity.

Text editors that can search one file can find some things.

Text editors that can search multiple files can find more hits.

IDEs that actually parse things can figure out where the definition is.

Better IDEs can find all semantic references to the same symbol without finding things that are just named the same but are part of a different type.

Still better IDEs can refactor code using even more advanced pattern matching. Edited by Nypyren

Share this post


Link to post
Share on other sites

I agree learning when to use it should be the end goal, but I can also imagine that you park it for some time, until you have a firmer grasp of the other new things.

 

Python was my first language that was reference-based, and it was mighty scary, so many new things at the same time. I decided to ignore sharing of data at first, and recomputed the new value for each variable from scratch every single time. While it cost performance, there were so many other things I needed to wrap my head around at the time, it made sense to me to postpone handling sharing as well.

 

As I got more confident in all the other concepts of the language, I started experimenting with less full recomputing (that is, use references in the code). Moved a bit too far, and reverted a step to eventually end up at the point where I am now, which seems to agree with how many other Python coders work.

Share this post


Link to post
Share on other sites

Here's how to generate a pseudorandom double. Much easier than including the Mersenne Twister header and cpp file.

#include <random>
#include <iostream>
 
using std::random_device;
using std::mt19937;
using std::uniform_real_distribution;
 
using std::cout;
using std::endl;
 
int main()
{
    random_device rd;
    mt19937 gen(rd());
    uniform_real_distribution<double> dis(0, 1);
    for (int n = 0; n < 10; ++n) {
        cout << dis(gen) << ' ';
    }
    cout << endl;
}
Edited by sjhalayka

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this