CS 372 Spring 2016 > Notes for January 25, 2016
CS 372 Spring 2016
Notes
for January 25, 2016
Modern C++: History
The C++ programming language was initially developed by Bjarne Stroustrup in 1983, as C with Classes.
The first ANSI/ISO standard was published in 1998. There was a minor update in 2003—bug/typo fixes only. These standards are referred to as C++98 and C++03, respectively.
A number of issues arose. In particular:
- C++ is aimed at efficiency. But C++ code often did unnecessary copying of temporary values.
- The forwarding problem:
making a generic function wrapper so that calling the wrapper
always has exactly the same effect as calling the wrapped function,
was impossible in C++98/03.
- Interestingly, the problem of unnecessary copying and the forwarding problem were solved with the same feature. We will discuss this later.
- In the STL, customizing an operation by passing a function
can be awkward
(e.g., passing a custom comparison to
std::sort
). - C++ was unable to take advantage of many advances in C.S.: type handling, functional programming, hash tables, etc.
- C++03 had no standardized, safe, clearly specified way to do multi-threaded programming.
- Two nice features, OO and generic programming, were difficult to use together.
- Various conventions became common for management of resources, writing certain member functions, etc., but there was no standardized support for these.
- Built-in arrays are icky, but sometimes unavoidable.
- Some aspects of the Standard Library became outdated (e.g., random-number generation).
To deal with such issues, in the mid 2000s an effort to produce a major revision of the C++ Standard was initiated. This was termed C++0x, in the expectation that it would be finished prior to 2010, and so the “x” would be replaced with a single digit. However, the new standard was actually completed in 2011; it is now known as C++11.
The plan is now to release a new C++ standard every three years. So a programming language that was once pretty much fixed is now all a-ferment again. C++14 was a relatively minor update. The C++17 effort is now underway; it is aimed at making more significant revisions.
We will look at some of the features added in C++11, with a few comments about C++14.
Modern C++: Various Features
Hash Tables
C++11 adds new containers
std::unordered_set
,
std::unordered_map
,
std::unordered_multiset
,
and
std::unordered_multimap
.
The iterfaces of these are similar to
std::set
, std::map
, etc.,
but the implementations are based on hash tables,
rather than balanced search trees.
The unordered set containers are declared in
standard header <unordered_set>
,
the unordered map containers are declared in
standard header <unordered_map>
,
See
usehash.cpp
for example code using C++11 hash-table containers.
Initializer Lists
C++ has always allowed built-in arrays to be initialized via a brace-enclosed list of values. This syntax was inherited from C.
[C++]
int arr[5] = { 6, 5, 4, 3, 2 };
C++11 extends this to arbitrary classes.
[C++]
vector<int> v = { 6, 5, 4, 3, 2 };
The type of the brace-enclosed list is
std::initializer_list<T>
,
where T
is the type of each item in the list.
This template is declared in standard header
<initializer_list>
.
To use this initialization syntax with your own C++ class,
write a constructor that takes an
initializer_list
as a parameter.
Fixed-Size Array Container: std::array
C++11 adds a new fixed-size array container:
std::array
,
declared in standard header <array>
.
[C++]
#include <array> using std::array;
std::array
works much like std::vector
,
except that it is not resizable,
and the size is given as a template parameter.
[C++]
array<int, 8> aa = { 9, 8, 7, 6, 5, 4, 3, 2 }; // std::array of size 8 // Sort items in aa sort(aa.begin(), aa.end());
Null Pointer Keyword: nullptr
C++11 adds the keyword nullptr
,
representing an arbitrary null pointer.
[C++]
int * p = nullptr;
The older syntax for a null pointer was to use “0
”
in a pointer context.
[C++]
int * p = 0; // Same as above
There was a standard macro NULL
,
but this was simply defined to be 0
.
[C++]
int * p = NULL; // Same as above
A problem with this syntax was that if 0
was used in a context where it could
mean either an int
or a pointer,
then it would be treated as an int
.
[C++]
void foo(int n) { cout << "int" << endl; } void foo(int * p) { cout << "pointer" << endl; } int main() { foo(NULL); // Before C++11, printed "int" (ICK!) }
I recommend always using nullptr
when a null pointer is intended.
Type Inference
C++11 adds features that allow a compiler to perform type inference: determining types without them being explicitly specified.
The first feature is a new use of the auto
keyword.
When a variable is declared, with a one-parameter constructor
being called, replace the type with auto
to make
the type of the variable the same as the type of the
constructor parameter.
[C++]
auto n = 3; // n is an int, since 3 is an int auto x = 3.2; // x is a double, since 3.2 is a double
Be careful with strings!
The type of a double-quoted literal is (const char *)
,
not std::string
.
[C++]
auto str = "abc"; // OOPS! str is (const char *) cout << str.size(); // DOES NOT COMPILE
A solution to this was provided in C++14.
Placing an s
after a double-quoted literal
converts it to a string
object.
This s
is actually an operator in the Standard Library.
It is declared in header <string>
,
and it is in namespace std::string_literals
.
[C++14]
#include <string> using namespace std::string_literals auto str = "abc"s; // str is a std::string
Note:
The s
suffix above
is an example of a user-defined literal.
These were actually introduced in C++11;
however, the C++11 Standard Library does not define
any examples of them.
Several were added to the Standard Library in C++14:
string-object literals,
and various time-duration units.
You can also define your own.
The second type-inference feature added to C++11
is the new keyword decltype
.
“decltype(
EXPR)
”,
where EXPR
is some expression,
represents the type of the expression.
[C++]
decltype(a+b) x; // x has same type as expression (a+b)
Range-Based For-Loops
C++11 introduces a new looping construct: the range-based for-loop. This can be used to iterate over any standard container.
[C++]
vector<int> v = { 10, 20, 30, 40 }; for (int i : v) { cout << i << " "; } cout << endl;
Above, i
goes through each of the values
in the vector
.
So the output will be as follows.
10 20 30 40
In practice, we almost never specify the type in a range-based for-loop;
instead, we use the auto
keyword.
[C++]
for (auto i : v) { ...
Any of the standard parameter-passing methods are also available.
[C++]
// By reference // Use when modifying items in the container. for (auto & i : v) { ... // By reference-to-const // Use when iterating through a container of objects // or items of unknown type. for (const auto & i : v) { ...
The range-based for-loop actually calls global functions
begin
and end
on the given
container.
These generally return container.begin()
and container.end()
,
respectively.
If you are writing a class that does not have
begin
and end
member functions,
but you still want it to be usable with range-based for-loops,
then you can define appropriate global functions
begin
and end
.
Initializer lists have these defined, so we can do the following.
[C++]
for (auto i : { 10, 20, 30, 40 }) { ...
Compile-Time Execution: constexpr
C++11 introduces
the new qualifier keyword constexpr
,
which marks values that are known at compile time.
[C++]
constexpr int n = 10;
The keyword can also qualify functions that can be executed at compile time.
[C++]
constexpr int sumSquares(int n) { return n <= 0 ? 0 : sumSquares(n-1) + n*n; }
When calling a constexpr
function
and storing its value,
be sure to store it in a constexpr
variable.
[C++]
constexpr int sumSquaresTo100 = sumSquares(100);
There are strong limitations on what code may be included
in a constexpr
function.
Only constexpr
functions may be called from a
constexpr
function.
In C++11, the function body of a constexpr
function
may consist only of a single return statement.
In C++14, this restriction was relaxed.
See
fibo_slow_constexpr.cpp
for example code using constexpr
.