Namespaces, Includes, and the Standard Library

A quick introduction to namespaces in C++, alongside the standard library and how we can access it

Ryan McCombe
Updated

This lesson is a quick introductory tour of functions within C++. It is not intended for those who are entirely new to programming. Rather, the people who may find it useful include:

  • those who have completed our introductory course, but want a quick review
  • those who are already familiar with programming in another language, but are new to C++
  • those who have used C++ in the past, but would benefit from a refresher

It summarises several lessons from our introductory course. Those looking for more thorough explanations or additional context should consider completing Chapters 7 and 8 of that course.

Intro to C++ Programming

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

The #include Directive

The most typical way we import code defined in another file is through an #include directive:

// Same Directory
#include "math.cpp"

// Child Directory
#include "some-subfolder/math.cpp"

// Parent Directory
#include "../math.cpp"

The include directive performs a crude copy-and-paste operation on our code before it is compiled. Because of this, we can just use included code as if it were in the same file we're working on:

// math.cpp
int add(int x, int y) {
  return x + y;
}
// main.cpp
#include "math.cpp"

int main() {
  Add(x+y);
}

In a project with a more complicated web of include directives, this recursive copy-and-paste process is liable to attempt to include the same file twice:

// geometry.cpp
float Pi{3.14f};
// math.cpp
#include "geometry.cpp"
// main.cpp
#include "geometry.cpp"
#include "math.cpp"

After resolving all the #include directives, main.cpp is including geometry.cpp and then indirectly including it again through math.cpp

This will result in a compilation error because we're now defining a variable called Pi twice. To solve this, for any file that is intended to be included in other files, we should add the #pragma once directive to ensure it never gets included more than once:

// geometry.cpp
#pragma once

float Pi{3.14f};
// math.cpp
#pragma once

#include "geometry.cpp"

Namespaces

Namespaces are the primary way to organize code in large applications. We give our namespace a meaningful name, and then open a set of braces, thereby creating a new scope.

namespace Math {
  // new scope
}

Within these braces, we can create variables, functions, and other constructs in the usual way:

namespace Math {
  int Add(int x, int y) {
    return x + y;
  }

  // Namespaces can be nested
  namespace Geometry {
    float Pi{3.14f};
  }
}

Scope Resolution Operator ::

Access to namespaces, and different scopes in general, can be done using the scope resolution operator ::

For example, to access a variable or function within the scope we called Math, we would prepend the identifier with Math::

Math::Add(1, 2);

Our Math namespace has a nested Geometry namespace, which we can access using multiple scope resolution operators:

Math::Geometry::Pi / 2;

Shadowed Variables

Multiple variables with the same name can be accessible within the same scope. In the following example, within the Math namespace, two variables identified by x are available:

int x{1};

namespace Math{
  int x{2};
}

We have the x defined within the global namespace, which has a value of 1, and the x defined within the local namespace, which has a value of 2

This is sometimes referred to as shadowed variables or shadowed identifiers. It is something we should try to avoid, as it can be confusing and cause bugs.

When resolving an identifier, the compiler will use the one in the scope that is most local to the expression that is using the identifier. As such, in the following example, Math::Value will be 2:

int x{1};

namespace Math{
  int x{2};
  int Value{x}; // 2
}

Access to the global scope is available using the unary scope resolution operator. That is, :: without a left operand. In the following example, Math::Value will now be 1:

int x{1};

namespace Math{
  int x{2};
  int Value{::x}; // 1
}

The Standard Library

C++ comes with a large standard library, which contains a large number of generally useful code. Standard library functionality is typically included using < and > as part of an #include directive.

One of the most common standard library features we will want is the ability to create strings:

// The standard library's string implementation
#include <string>

Strings, and most standard library types and functionality are available within the std namespace:

#include <string>

int main() {
  std::string Greeting{"Hello"};
}

Streaming Output using std::cout

By including <iostream> we get access to some useful input and output functionality.

// Ways to interact with input and output
#include <iostream>

The std::cout object allows us to stream content to our terminal using the << operator, thereby letting us see program output:

#include <string>
#include <iostream>

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

This program yields the following output:

Hello

The << operator returns the reference to std::cout, so we can keep streaming content to it as a larger expression:

#include <string>
#include <iostream>

int main() {
  std::string Greeting{"Hello"};

  // The << operator can be chained
  std::cout << Greeting << " World";
}
Hello World

We can insert line breaks into our output using the \n sequence, or by streaming a std::endl token:

#include <iostream>

int main() {
  std::cout << "After this, there is a break\n";
  std::cout << "after this too!" << std::endl;
  std::cout << "This will\nspan two lines";
}
After this, there is a break
after this too!
This will
span two lines

Reducing Syntax with using Statements

When we're repeatedly going to be using a variable or function from a namespace, it can be helpful to add a using statement. This can be within the global scope, where it will apply to an entire file, or just within the scope of a specific block:

void MyFunction() {
  using std::cout, std::endl;

  // In the rest of this function, we can now
  // use cout and endl without needing to
  // specify the std:: prefix
  cout << "This works!" << endl;
}

If we want to refer to a whole namespace without qualification, we can have a using namespace statement:

void MyFunction() {
  using namespace std;

  // Now, anything in the std namespace can be
  // used, without needing prefixed with std::
  cout << "This works!" << endl;
}

The using namespace statement should generally be restricted, as it bypasses the purpose of having a namespace in the first place.

When files get more complex, it can be difficult to determine where functions and variables are coming from if we omit namespace qualifiers.

If we do want to have using namespace statements, we should prefer using them just within the specific function we need them, rather than across an entire file.

This is particularly true when we're writing code that we are going to #include in other files, because then our global using statement will affect those files, too.

Literals in Namespaces

Another scenario where we may want to use a using namespace statement is to gain access to more literals.

Under the hood, literals are simply functions, and are typically defined within namespaces. We'll see examples of this later in the course, when we learn to create literals for our own custom types.

For now, we can see an example of this using a literal for creating std::string, which is available within the std namespace.

We gain access to it through an appropriate using namespace statement, such as:

  • using namespace std
  • using namespace std::literals
  • or using namespace std::string_literals

In any scope where such a statement is in effect, we can use double-quotes with an s suffix, such as "Hello"s to create a std::string:

#include <string>

int main(){
  using namespace std::string_literals;

  // Creates a std::string
  auto MyString{"Hello World"s};
}

Summary

In this lesson, we explored the basics of organizing code using namespaces. We also discovered how to use #include directives to use code from other files and libraries, and how to use the standard library to access powerful pre-built functionality. Key takeaways:

  • Namespaces help avoid naming conflicts and organize code logically.
  • The #include directive allows reusing code from other files and libraries.
  • The scope resolution operator :: is used to access elements in a namespace.
  • Shadowed variables occur when an identifier in an inner scope has the same name as an identifier in an outer scope.
  • The standard library provides a rich set of pre-built functionality.
  • std::cout and std::endl are used for console output.
  • using statements allow using names from a namespace without explicit qualification.
  • Literals for std::string are created using the s suffix - for example: "Hello World"s.
Next Lesson
Lesson 4 of 128

Conditionals and Loops

Learn the fundamentals of controlling program flow in C++ using if statements, for loops, while loops, continue, and break

Questions & Answers

Answers are generated by AI models and may not have been reviewed. Be mindful when running any code on your device.

Why Use Namespaces in C++?
What are the benefits of using namespaces in C++? When should I use them in my code?
Multiple using Statements
Can I have multiple using statements for the same namespace in different scopes? What happens if I do?
Setting Include Paths
How do I set up my compiler to find header files in different directories?
Namespace Aliases
What is a namespace alias and when would I use one?
Header Include Order
Does the order of #include directives matter? If so, what's the correct order?
Explicit std:: Namespace
Why do you use explicit std:: namespace qualifiers in this lesson's code examples instead of 'using namespace std;'?
Or Ask your Own Question
Get an immediate answer to your specific question using our AI assistant