Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CPlusPlusNotesForProfessionals.pdf
Скачиваний:
47
Добавлен:
20.05.2023
Размер:
5.11 Mб
Скачать

Chapter 44: Namespaces

Used to prevent name collisions when using multiple libraries, a namespace is a declarative prefix for functions, classes, types, etc.

Section 44.1: What are namespaces?

A C++ namespace is a collection of C++ entities (functions, classes, variables), whose names are prefixed by the name of the namespace. When writing code within a namespace, named entities belonging to that namespace need not be prefixed with the namespace name, but entities outside of it must use the fully qualified name. The fully qualified name has the format <namespace>::<entity>. Example:

namespace Example

{

const int test = 5;

const int test2 = test + 12; //Works within `Example` namespace

}

const int test3 = test + 3; //Fails; `test` not found outside of namespace. const int test3 = Example::test + 3; //Works; fully qualified name used.

Namespaces are useful for grouping related definitions together. Take the analogy of a shopping mall. Generally a shopping mall is split up into several stores, each store selling items from a specific category. One store might sell electronics, while another store might sell shoes. These logical separations in store types help the shoppers find the items they're looking for. Namespaces help c++ programmers, like shoppers, find the functions, classes, and variables they're looking for by organizing them in a logical manner. Example:

namespace Electronics

{

int TotalStock; class Headphones

{

// Description of a Headphone (color, brand, model number, etc.)

};

class Television

{

// Description of a Television (color, brand, model number, etc.)

};

}

namespace Shoes

{

int TotalStock; class Sandal

{

// Description of a Sandal (color, brand, model number, etc.)

};

class Slipper

{

// Description of a Slipper (color, brand, model number, etc.)

};

}

There is a single namespace predefined, which is the global namespace that has no name, but can be denoted by ::. Example:

GoalKicker.com – C++ Notes for Professionals

237

void bar() {

// defined in global namespace

}

namespace foo { void bar() {

// defined in namespace foo

}

void barbar() {

bar(); // calls foo::bar()

::bar(); // calls bar() defined in global namespace

}

}

Section 44.2: Argument Dependent Lookup

When calling a function without an explicit namespace qualifier, the compiler can choose to call a function within a namespace if one of the parameter types to that function is also in that namespace. This is called "Argument Dependent Lookup", or ADL:

namespace Test

{

int call(int i); class SomeClass {...};

int call_too(const SomeClass &data);

}

call(5); //Fails. Not a qualified function name. Test::SomeClass data;

call_too(data); //Succeeds

call fails because none of its parameter types come from the Test namespace. call_too works because SomeClass is a member of Test and therefore it qualifies for ADL rules.

When does ADL not occur

ADL does not occur if normal unqualified lookup finds a class member, a function that has been declared at block scope, or something that is not of function type. For example:

void foo(); namespace N {

struct X {};

void foo(X ) { std::cout << '1'; } void qux(X ) { std::cout << '2'; }

}

struct C {

void foo() {} void bar() {

foo(N::X{}); // error: ADL is disabled and C::foo() takes no arguments

}

};

void bar() {

extern void foo(); // redeclares ::foo

foo(N::X{}); // error: ADL is disabled and ::foo() doesn't take any arguments

GoalKicker.com – C++ Notes for Professionals

238

}

int qux;

void baz() {

qux(N::X{}); // error: variable declaration disables ADL for "qux"

}

Section 44.3: Extending namespaces

A useful feature of namespaces is that you can expand them (add members to it).

namespace Foo

{

void bar() {}

}

//some other stuff

namespace Foo

{

void bar2() {}

}

Section 44.4: Using directive

The keyword 'using' has three flavors. Combined with keyword 'namespace' you write a 'using directive':

If you don't want to write Foo:: in front of every stu in the namespace Foo, you can use using namespace Foo; to import every single thing out of Foo.

namespace Foo

{

void bar() {} void baz() {}

}

//Have to use Foo::bar() Foo::bar();

//Import Foo

using namespace Foo; bar(); //OK

baz(); //OK

It is also possible to import selected entities in a namespace rather than the whole namespace:

using Foo::bar;

bar(); //OK, was specifically imported baz(); // Not OK, was not imported

A word of caution: using namespaces in header files is seen as bad style in most cases. If this is done, the namespace is imported in every file that includes the header. Since there is no way of "un-using" a namespace, this can lead to namespace pollution (more or unexpected symbols in the global namespace) or, worse, conflicts. See this example for an illustration of the problem:

/***** foo.h *****/

GoalKicker.com – C++ Notes for Professionals

239

namespace Foo

{

class C;

}

/***** bar.h *****/

namespace Bar

{

class C;

}

/***** baz.h *****/

#include "foo.h" using namespace Foo;

/***** main.cpp *****/

#include "bar.h" #include "baz.h"

using namespace Bar;

C c; // error: Ambiguity between Bar::C and Foo::C

A using-directive cannot occur at class scope.

Section 44.5: Making namespaces

Creating a namespace is really easy:

//Creates namespace foo namespace Foo

{

//Declares function bar in namespace foo void bar() {}

}

To call bar, you have to specify the namespace first, followed by the scope resolution operator :::

Foo::bar();

It is allowed to create one namespace in another, for example:

namespace A

{

namespace B

{

namespace C

{

void bar() {}

}

}

}

Version ≥ C++17

The above code could be simplified to the following:

namespace A::B::C

{

void bar() {}

GoalKicker.com – C++ Notes for Professionals

240