Operators

Operators are unique symbols that are used to perform changes to data. They often have infix notation with some having prefix or postfix notation. In C++, all operators are functions however, they are built into the language fpr primitive data types.

Basic Arithmetic

So enough about types and values. Lets write some code that does something. In C++ there are a lot, and I mean a lot of operators but we will only cover the arithmetic based one this week. The first ones we will look at are the basic arithmetic operators. These include your standard:

  • + - Addition
  • - - Subtraction
  • * - Multiplication
  • / - Division
  • % - Modulo

For the meanwhile we will only look at operations on integers and floating point types. For these types the operators do what you would expect. Try out the following operations.

#include <iostream>

auto main () -> int
{
    auto a{10};
    auto b{3};

    std::cout << "a + b = " << a + b << std::endl;  ///< a + b = 13
    std::cout << "a - b = " << a - b << std::endl;  ///< a - b = 7
    std::cout << "a * b = " << a * b << std::endl;  ///< a * b = 30
    std::cout << "a / b = " << a / b << std::endl;  ///< a / b = 3??
    std::cout << "a % b = " << a % b << std::endl;  ///< a % b = 1

    return 0;
}

Example

Note: for those unaware, % returns the remained of the division of \( \frac{a}{b} \)

But hold up, why does a / b return 3, should it not be 3.33...? This correct.. sorta. In C++ when two integers divide it performs integer division, thus throwing away any remainder after the maximum even divisions. This is the same as Pythons // operator. To perform floating point division, either the numerator or denominator needs to be of a floating point type. This is so the alternate one (if it is an integer type) can be promoted to a floating point type to perform the floating point division.

#include <iostream>

auto main () -> int
{
    auto a{10};
    auto b{3};
    auto c{3.};
    auto d{10.};

    std::cout << "a / c = " << a / c << std::endl;  ///< a / c = 3.33333
    std::cout << "d / b = " << d / b << std::endl;  ///< d / b = 3.33333

    return 0;
}

Example

Note: The modulo operator does not work for floating point types as this illogical (can't return remainder of a floating point division as it is near impossible to regain the information).

You can also use + and - to change/force the sign of a integer or floating point type.

#include <iostream>

auto main () -> int
{
    auto e{-5.43};
    auto f{0.71};

    std::cout << "e + f = " << e + f << std::endl;          ///< e + f = -4.72
    std::cout << "-e + f = " << -e + f << std::endl;        ///< -e + f = 6.14
    std::cout << "e - -f = " << e - f << std::endl;         ///< e - f = -6.14
    std::cout << "e - -f = " << e - -f << std::endl;        ///< e - -f = -4.72

    return 0;
}

Example

There are also in-place operators ++ and --. These allow you to increment/decrement integer types in place. There are two variations, prefix and postfix. Prefix will increment/decrement the value and then provide a lvalue of the new value of the object to whatever is reading it (if any). Postfix will provide an lvalue to copy of the old value and then increment/decrement the value.

#include <iostream>

auto main () -> int
{
    auto g{1};
    auto h{5};

    std::cout << "g++ = " << g++ << std::endl;      ///< g++ = 1
    std::cout << "g = " << g << std::endl;          ///< g = 2
    std::cout << "++g = " << ++g << std::endl;      ///< ++g = 3
    std::cout << "g = " << g << std::endl;          ///< g = 3

    std::cout << "h-- = " << h-- << std::endl;      ///< h-- = 5
    std::cout << "h = " << h << std::endl;          ///< h = 4
    std::cout << "--h = " << --h << std::endl;      ///< --h = 3
    std::cout << "h = " << h << std::endl;          ///< h = 3

    return 0;
}

Example

Casts

In C++ you can change the type of an object via casting. There are quite a few different casting operators.

  • const_cast<T>(expr) - Changes cv-qualifications (cv := const-volatile)
  • static_cast<T>(expr) - Attempts to cast expr entirely different type T.
  • reinterpret_cast<T>(expr) - Reinterprets the underlying bit pattern of expr.
  • dynamic_cast<T>(expr) - Allows for casting up, down and sideways through class hierarchies.

Note: T is the type that the expr is being cast to.

You will likely not come across needing to any of the casts except static_cast<T>(expr). Reach for this first.

#include <iostream>

auto main () -> int
{
    auto a{10};
    auto b{3};

    /// Explicitly cast `b` to a `double`
    std::cout << "a / b = " << a / static_cast<double>(b) << std::endl;  ///< a / b = 3.33333

    return 0;
}

Example

Bitwise Operations

In C++ there is another category of operators called bitwise operators. These operators only apply to integer types but allow for you to individually control the bits of an integer.

  • & - Bitwise And
  • | - Bitwise Or
  • ^ - Bitwise Xor
  • << - Bitwise Left Shift (Left Rotate)
  • >> - Bitwise Right Shift (Right Rotate)

Note: We've seen << before with std::cout. In the case of std::cout << means 'put (to)'. It is simply an overloaded operator used for ease of use. It doesn't correlate to the bitwise meaning.

Each of the bitwise operators perform their respective logical operations on each of the bits the the two values or points and returns the new value.

#include <bitset>
#include <iostream>

auto main () -> int
{
    auto i{5};
    auto j{4};

    std::cout << "i & j = " << (i & j) << std::endl;                            ///< i & j = 4
    std::cout << "  " << std::bitset<8>{i} << std::endl;
    std::cout << "& " << std::bitset<8>{j} << std::endl;
    std::cout << "----------" << std:: endl;
    std::cout << "  " << std::bitset<8>{i & j} << std::endl;                    ///< i & j =  00000100

    std::cout << "i | j = " << (i | j) << std::endl;                            ///< i | j = 4
    std::cout << "  " << std::bitset<8>{i} << std::endl;
    std::cout << "| " << std::bitset<8>{j} << std::endl;
    std::cout << "----------" << std:: endl;
    std::cout << "  " << std::bitset<8>{i | j} << std::endl;                    ///< i | j =  00000101

    std::cout << "i ^ j = " << (i ^ j) << std::endl;                            ///< i ^ j = 4
    std::cout << "  " << std::bitset<8>{i} << std::endl;
    std::cout << "^ " << std::bitset<8>{j} << std::endl;
    std::cout << "----------" << std:: endl;
    std::cout << "  " << std::bitset<8>{i ^ j} << std::endl;                    ///< i ^ j =  00000001

    std::cout << "i << j = " << (i << j) << std::endl;                          ///< i << j = 4
    std::cout << "   " << std::bitset<8>{i} << std::endl;
    std::cout << "<< " << std::bitset<8>{j} << std::endl;
    std::cout << "-----------" << std:: endl;
    std::cout << "   " << std::bitset<8>{i << j} << std::endl;                  ///< i << j =  01010000

    std::cout << "i >> j = " << (i >> j) << std::endl;                          ///< i >> j = 4
    std::cout << "   " << std::bitset<8>{i} << std::endl;
    std::cout << ">> " << std::bitset<8>{j} << std::endl;
    std::cout << "-----------" << std:: endl;
    std::cout << "   " << std::bitset<8>{i >> j} << std::endl;                  ///< i >> j =  00000000

    return 0;
}

Example

A bit about shift operations

For the shift operations, the general pattern is as follows <shifted> <shift-op> <additive>. This means the value that is being shifted is always on the left-hand-side and is always shifted by the number indicated on the right-hand-side. For left-shifts, the bit pattern is moved N spot to the left, pushing zeros at the end of the right side and popping any bit off the left end. For right shifts, the opposite occurs. The bit pattern is move right by N spots, popping any bit off the right end and push the same bit as the sign bit of the number being shifted (1's if negative and 0's if positive).

Arithmetic Assignment

There is one final set of arithmetic operators in C++. These are the arithmetic assignment operators. These will perform the operation between two points and assign the result to the left point.

  • += - Add assign - a = a + b == a += b
  • -= - Subtract assign - a = a - b == a -= b
  • *= - Multiply assign - a = a * b == a *= b
  • /= - Divide assign - a = a / b == a /= b
  • %= - Modulo assign - a = a % b == a %= b
  • &= - And assign - a = a & b == a &= b
  • |= - Or assign - a = a | b == a |= b
  • ^= - Xor assign - a = a ^ b == a ^= b
  • <<= - Left-shift assign - a = a << b == a <<= b
  • >>= - Right-shift assign - a = a >> b == a >>= b
#include <iostream>

auto main () -> int
{
    auto k{5};
    auto l{2};

    k += l;
    std::cout << "k += l -> k = " << k << std::endl;    ///< k = 7

    k *= l;
    std::cout << "k *= l -> k = " << k << std::endl;    ///< k = 14

    l |= k;
    std::cout << "l |= k -> l = " << l << std::endl;    ///< l = 14

    k <<= l;
    std::cout << "k <<= l -> k = " << k << std::endl;   ///< k = 229376

    l ^= k;
    std::cout << "l ^= k -> l = " << l << std::endl;    ///< l = 229390

    k &= l;
    std::cout << "k &= l -> k = " << k << std::endl;    ///< k = 229376

    l -= k;
    std::cout << "l -= k -> l = " << l << std::endl;    ///< l = 14

    return 0;
}

Example

Have a play with these operators and try and perform some computations that you might do in another languages.

Size Operator

Another useful operator is the sizeof and sizeof... operator. It returns the number of bytes if a type parameter pack (more on parameter packs later).

#include <iostream>

auto main () -> int
{
    auto a {10};
    auto b {3.5};
    auto c {'c'};

    std::cout << "sizeof (a) = " << sizeof (a) << std::endl;  ///< sizeof (a) = 4
    std::cout << "sizeof (b) = " << sizeof (b) << std::endl;  ///< sizeof (b) = 8
    std::cout << "sizeof (c) = " << sizeof (c) << std::endl;  ///< sizeof (c) = 1

    return 0;
}

Example