One of my clients made fun of me recently for saying Excel is Excel. Tautologies are true statements and mathematicians have a propensity for them. What I was failing to communicate effectively was that all software tools have their limitations and you need to learn their quirks if you want to get the most out of them.
Software is written by people. Not quite a tautology these days, but close. It is still a cottage industry that requires humans with a pile of grey matter on top of a what is essentially a monkey brain to tap away at keyboards.
I learned some lessons tonight while teaching a course that uses C++ as a tool for implementing algorithms. People tend to stumble over simple things and bring preconceived notions from languages they already know. They get frustrated when C++ doesn’t work like their favorite language.
Programming languages are not religions, they are tools to help you achieve your ends. Learn your tools, don’t be one.
There is no substitute for spending time learning your tools. If you know nothing about C++, Programming – Principles and Practice Using C++ is a good place to start. If you know some C++ and want to get up to speed with the latest goodness in C++2011, consult Elements of Modern C++ Style. The C++ glossary is a handy resource also straight from the horses mouth.
; — Semicolon
By far the most common mistake I see is people forgetting semicolons. They end statements in C++. C++ is space insensitive, so a newline (or tab, or two or more consecutive spaces) provide no information to the compiler. Semicolons tell the compiler to do what you tell it to do. x = x + 1;
increments x
by 1. Semicolon means ‘do it’.
As a mathematician, that is like fingernails scratching on a blackboard. Why not x <- x + 1;
? Anyone can propose a change to C++ at http://isocpp.org/. And they do.
{ } — Curly Braces
Curly braces are used for several different things in C++. They can be used in several different ways so pay attention!
They are used with class
es and struct
s to group data and functions, in which case the closing curly brace must be followed with a semicolon.
They are also used with functions for the body of code. These don’t need to end with a semicolon.
Another use is for blocks of expressions associated with control flow structures: if (e) { ...; } else { ...; }
If the block contains only one statement the braces are optional, but go ahead and use them anyway. It will help prevent bugs if you or someone else decides to add another statement to the block later.
Namespaces are enclosed in curly braces too. No need for a semicolon afterwards.
Finally, they are also used for uniform initialization — a very handing thing to make yourself familiar with.
See also the previous blog post.
( ) — Parentheses
These are used when declaring or defining a function and when calling a function. The difference is that when declaring or defining a function you have to tell the compiler the types of the function arguments, whereas when you call the function you only need to pass the values. Also, don’t forget you can declare (functions and data) as many times as you want, but only define them once. See the one definition rule
[ ] — Square Brackets
Of course you know these are for indexing into arrays. Maybe you didn’t know the stupid C trick that 2["unix"]
evaluates to the character 'i'
. (The reason is that a[b]
is converted to *(a + b)
by the compiler.) They are also used to introduce lambda expressions since C++2011. When you type [](int i) { return 2*i; }
the compiler generates a class with an overloaded operator()
. E.g.,
class lambda_funny_name_you_cannot_know {
public:
int operator()(int i) { return 2*i; }
};
But what if you need to use some variables that are in scope at the time the function is created? Just list those inside the square brackets. By default, the values are copied into corresponding members of the generated class. It is possible to pass them by reference, but don’t do that. Non-local side effects make it more difficult to reason about code correctness.
*,& — pointers and references
Given a value like int i{42}; // <-- don't forget the semicolon!
the ampersand operator, &
gives you the address of the value i
. This address is called a pointer. You can capture the pointer with int* pi = &i;
. Given a pointer you can get the value back by dereferencing the pointer: assert (*pi == 42);
.
A reference is just an alias: int& ri{i};
. If you execute, e.g., ri = 24;
, then i
now has the value 24
, not 42
. If the reference is const
, e.g., const int& cri{i}
and you try to change the value through the reference, cri = 24;
you will get a compiler error.
r-values and l-values
An l-value is a value that has a name associated with it and an r-value is a value without a name associated with it, e.g., a temporary value. If int i{1},j{2};
then i + j
has the value 3, but no name, hence it is an r-value. The name comes from the fact l-values typically occur on the left hand side of assignments and r-values occur on the right hand side, int k; k = i + j;
Semi-colons don’t mean “do it”. They are about re-sync after a parsing error. Forgetting a semi-colon isn’t a hard bug to find so I fail to see why you seem to have an issue with them.
I don’t have an issue with them, it just seemed to be a common mistake I saw people making. If you have a better mental crutch to provide people still learning the language feel free to improve on my effort.