The using Keyword

This lesson introduces the using keyword in C++, focusing on namespaces, enums, and type aliasing
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, Unlimited Access
3D art showing a fantasy character
Ryan McCombe
Ryan McCombe
Updated

When dealing with excessively verbose code, C++ provides a few options for us to reduce the noise. Typically, these options have the using syntax. We’ll explore the four main uses for it in this lesson:

  • Reducing the amount of code we need to write when working with namespaces
  • Reducing the amount of code we need to write when working with enums
  • Changing the types our program uses based on a preprocessor definition
  • Giving complex types shorter or more meaningful names

using namespace Statements

Starting from the very first lesson, we've had a line of code in our files that we've left unexplained thus far:

using namespace std;

Predictably, this line is related to namespaces.

The effect of this using declaration was an instruction that asks the compiler: "if an identifier we use is not found, try searching for it in the std namespace".

This using statement has been necessary every time we used cout and string. This is because the standard library functionality, including these identifiers, is in the std namespace:

#include <iostream>

int main() {
  std::string MyString{"Hello"};
  std::cout << MyString;
}

By adding a using namespace std declaration, we have the option of removing the std:: prefix:

#include <iostream>
using namespace std;

int main() {
  string MyString{"Hello"};
  cout << MyString;
}

We can use using namespace declarations with any namespace - not just std:

namespace Maths {
  float Pi { 3.14f };
}

using namespace Maths;
using namespace std;

int main() {
  // equivalent to std::cout << Maths::Pi
  cout << Pi;
}
Test your Knowledge

Using Namespaces

What statement can we add below to make our call to Square() work?

namespace Utilities {
  void Square() {};
};

// ?

Square();

Restrict using Declarations to Specific Identifiers

Rather than having a using statement that covers the entire std namespace, we can also restrict it just to specific identifiers.

For example, to use it with just std::cout and std::string, we could write this:

#include <iostream>

using std::cout, std::string;

int main() {
  string MyString{"Hello World"};
  cout << MyString;
}
Hello World

Should I use using namespace Statements?

Probably not. Namespaces exist to help us organize code - a using namespace declaration effectively subverts that organization.

Remember too how #include directives work - if we have a using namespace declaration that is scoped to an entire file, that would also affect any file that includes it.

This can cause things to get out of hand quite quickly so, if using namespace declarations are going to be adopted, be particularly cautious when using them on files that are likely to be included in other files.

The Unreal coding standard asks for using declarations to never be used in the global scope. We talk about non-global using statements later in this lesson:

Do not put using declarations in the global scope, even in a .cpp file. It's okay to put using declarations within another namespace, or within a function body.

And the Google C++ standard asks that using declarations never be used, anywhere:

Do not use using-directives (e.g., using namespace foo).

If we want to use using statements, there are two ways we can get the benefits whilst reducing many of the drawbacks.

  • Restrict them to the specific identifiers we need - eg using std::string.
  • Reduce the scope of the using statement, which we cover in the next lesson

Scoped using Declarations

All of the using declarations we cover in this lesson follow the normal block scoping rules of other statements. A using declaration outside of any block has a "global" scope and will apply to the entire file.

This can be problematic, especially if that file is then added to further files, via the #include directive.

As an alternative to global using declarations, we can place them inside blocks - usually function bodies or other namespaces. This will limit their effect to just the scope of that block.

#include <iostream>

void Hello() {
  // This using statement's effects are
  // limited to just this function block
  using namespace std;
  cout << "Hello ";
}

void World() {
  // Error - there are no using
  // statements in effect in this scope
  cout << "World";  
}

int main() {
  Hello();
  World();
}
error C2065: 'cout': undeclared identifier

using enum Statements (C++20)

In some files, we may want to use the same enum repeatedly, and the constant qualification of MyEnumName:: would make our code excessively verbose.

We can solve this problem in the same we we tacked the equivalent issue with namespaces - by adding a using statement - specifically a using enum statement:

This can take our code from something like this:

enum class Faction { Human, Elf, Undead };

Faction MyFaction { Faction::Human };

To this:

enum class Faction { Human, Elf, Undead };

using enum Faction;

Faction MyFaction { Human };

Note, using enum is a relatively recent addition to the language, included as part of the C++20 spec. In projects that use older compilers, it will not be available.

Test your Knowledge

Using Enums

What statement can we add below that would make our Character declaration valid?

enum class Hostility { Friendly, Neutral, Hostile };

// ?

class Character {
  Hostility Hostility { Friendly };
};

Type aliases with using

The final scenario where we want to demonstrate a using statement is for type aliasing.

This allows us to create a pseudonym for a type we want to use in our code:

using integer = int;

int main() {
  integer MyNumber{42};
}

There are two main uses for this.

1. Switching Types at Compile Time

The first use case is to change what types we use based on a preprocessor definition.

For example, perhaps we want the specific type of integer we’re using to be different between platforms.

Rather than scattering that logic throughout our code, we could keep it in one place.

Below, we’re using int64_t and int32_t, which work like the int type we’ve been using so far. The only difference is that int64_t and int32_t are fixed-width integers.

That is, they specify exactly how many bits they use, rather than letting the platform decide:

#pragma once
#include <cstdint>

#ifdef PLATFORM_64BIT
  using integer = std::int64_t;
#else
  using integer = std::int32_t;
#endif

We then #include this file in all our other files, and use our new integer alias like any other type:

#include "types.h"

int main() {
  integer MyNumber{42};
}

2. Aliasing Complex Types

The second use case for type aliases with the using statement is to give complex types shorter, or more meaningful names.

This is of limited use for now when we’re dealing with simple types like int and string but, in C++, types can get fairly complex.

In the future, we’ll see types like the one used by this Inventory variable:

std::unordered_map<
  Enums::ItemType, std::vector<Item&>
> Inventory;

Scattering a type like this through our function prototypes in a class declaration, for example, would get very messy. So, we can use a using statement to create a simpler alias:

using Items = std::unordered_map<
  Enums::ItemType, std::vector<Item&>>;

class Character {
public:
  void SetInventory(Items Inventory) {
    mInventory = Inventory;
  }
  Items GetInventory() { return mInventory; }
private:
  Items mInventory;
};

Type Aliases using typedef

There is another way to create type aliases, which uses the typedef syntax:

typedef int integer;

int main() {
  integer MyNumber;
}

The typedef syntax was inherited from the C language but is still commonly used. We tend to stick with using, as it is compatible with templates, an advanced C++ feature we’ll introduce in the next course.

Summary

This lesson delves into the use of using directives, exploring their applications in reducing verbosity, managing namespaces, and simplifying code through type aliasing

Key Learnings:

  • The using namespace statement allows for shorter code by omitting the namespace prefix, particularly with the standard (std) namespace.
  • Scoped using declarations can be confined to specific blocks like function bodies, reducing their global impact.
  • The using enum statement simplifies enum usage by eliminating the need for enum class qualification.
  • Type aliasing with using enables switching types at compile time and giving complex types more meaningful names.
  • Caution around the using namespace statement is recommended, particularly in the global scope.

Preview of the Next Chapter

In our upcoming chapter, we'll explore the principles of creating robust, maintainable code. We'll delve into topics like:

  • Simplifying variable declarations and improving code readability with automatic type inference.
  • Understanding and implementing const-correctness.
  • Utilizing automatic code formatting tools to maintain consistent coding styles.
  • Leveraging static analysis for identifying potential errors and code smells.
  • Employing attributes for hints to the compiler and colleagues.
  • Formatting comments using Javadoc, enabling additional functionality in IDEs and other tools.

Was this lesson useful?

Next Lesson

Automatic Type Deduction using auto

This lesson covers how we can ask the compiler to infer what types we are using through the auto keyword
3D art showing a fantasy maid character
Ryan McCombe
Ryan McCombe
Updated
3D art showing a progammer setting up a development environment
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, Unlimited Access
Namespaces and Enums

    42.
    The using Keyword

    This lesson introduces the using keyword in C++, focusing on namespaces, enums, and type aliasing


3D art showing a progammer setting up a development environment
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access

This course includes:

  • 56 Lessons
  • Over 200 Quiz Questions
  • 95% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Next Lesson

Automatic Type Deduction using auto

This lesson covers how we can ask the compiler to infer what types we are using through the auto keyword
3D art showing a fantasy maid character
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved