Navigation



B.2 — Long long, auto, decltype, nullptr, and enum classes

Type long long

In you recall from lesson 2.4 — Integers, the largest integer type C++03 defines is “long”. Long has a platform-specific size that can be either 32 or 64 bits. C++ defines a new type named long long that’s guaranteed to be at least 64 bits in length. Because “long long” was already introduced by C99, many compilers already supported it prior to C++11.

Strangely enough, although C++11 imported long long from C99, they opted not to import fixed-width integers.

Type inference with auto and decltype

My favorite change in C++11 is the introduction of the auto keyword. Consider the common use case where you want to iterate through a vector using a for loop:

for (std::vector<int>::const_iterator itr = myvector.cbegin(); itr != myvector.cend(); ++itr)

Having to determine that the data type for the iterator itr is “std::vector::const_iterator” is both a pain to get correct and obnoxious considering that the compiler already knows that the return type from cbegin() is std::vector::const_iterator — but it makes you type it out anyway.

That’s where the auto keyword comes in:

for (auto itr = myvector.cbegin(); itr != myvector.cend(); ++itr)

The auto keyword tells the compiler to infer the type of the variable from its initializer.

auto x = 5; // x will be type int
auto y = 5.5; // y will be type double
auto z = y; // z will be type double
auto w = "hi"; // w will be type const char*

The decltype can be used to determine the type of an expression at compile-type.

decltype(5) x; // x will be type int because 5 is an int
decltype(x) y = 6; // y will be type int because x is an int
auto z = x; // z will type type int

Although it may seem like auto and decltype will always deduce the same type, that isn’t the case, as shown by the following example:

const std::vector<int> v(5); // declare a vector v
auto a = v[0]; // a will be type int because v[0] is an int
decltype(v[0]) b = 1; // b will be type const int&, which is the return type of std::vector<int>::operator[](size_type) const

Generally, if you need a type for a variable you are going to initialize, use auto. decltype is better used when you need the type for something that is not a variable, like a return type.

Type nullptr

In previous iterations of C and C++, 0 acted as both a constant integer and as the null pointer constant, which is why the following oddity occurs:

int *p = 1; // illegal, can't assign an int to an int* variable
int *q = 0; // legal, 0 has a special meaning as a null pointer

C++11 defines a new reserved identifier called nullptr (of type nullptr_t) that is not an integer, and can not be converted to an integer (though oddly enough, it can be converted to the boolean value false). 0 remains a valid null point constant for backwards compatibility purposes.

Enum classes

(Note: The following isn’t yet supported by Visual Studio 2010, but it’s simple enough to follow even without trying the examples yourself)

In C++03, enums are not type safe — they are treated as integers even when the enumeration types are distinct. Consider the following case:

#include <iostream>
using namespace std;

int main()
{
    enum Color
    {
        RED,
        BLUE
    };

    enum Fruit
    {
        BANANA,
        APPLE
    };

    Color a = RED;
    Fruit b = BANANA;

    if (a == b) // The compiler will compare a and b as integers
        cout << "a and b are equal" << endl; // and find they are equal!
    else
        cout << "a and b are not equal" << endl;

    return 0;
}

When C++ compares a and b, it’s comparing them as integers, which means in the above example, a does equal b since they both default to integer 0. This is definitely not as desired since a and b are from different enumerations!

C++11 defines a new concept, the enum class, which makes enums both strongly typed and strongly scoped.

int main()
{
    enum class Color
    {
        RED,
        BLUE
    };

    enum class Fruit
    {
        BANANA,
        APPLE
    };

    Color a = Color::RED; // note: RED is not accessible any more, we have to use Color::RED
    Fruit b = Fruit::BANANA; // note: BANANA is not accessible any more, we have to use Fruit::BANANA

    if (a == b) // compile error here, as the compiler doesn't know how to compare different types Color and Fruit
        cout << "a and b are equal" << endl;
    else
        cout << "a and b are not equal" << endl;

    return 0;
}

With normal enums, you can access enumerators (eg. RED) directly in the surrounding scope (eg. within main). However, with enum classes, the strong scoping rules mean you have to use a scope qualifier to access the enumerator (eg. Color::RED). This helps keep name pollution and the potential for name conflicts down.

The strong typing rules means that C++ will look for an explicitly defined comparison function to compare Color and Fruit. Since we haven’t defined an operator==(Color, Fruit) function, the compiler won’t understand how to compare a and b in any meaningful way, and this will cause a compile-time error to occur.

B.3 — Range-based for statements and static assert
Index
B.1 — Introduction to C++11

9 comments to B.2 — Long long, auto, decltype, nullptr, and enum classes

You must be logged in to post a comment.