B. Stroustrup - The C++ Programming Language (794319), страница 87
Текст из файла (страница 87)
That (nonlocal)lookup is not affected by Text_lib’s Glyph and Line.To refer to members of a namespace, we can use its fully qualified name. For example, if wewant a glyph() that uses definitions from Text_lib, we can write:Text_lib::Glyph glyph(Text_lib::Line& ln, int i);// ln[i]Other ways of referring to members from outside their namespace are using-declarations (§14.2.2),using-directives (§14.2.3), and argument-dependent lookup (§14.2.4).14.2.1 Explicit QualificationA member can be declared within a namespace definition and defined later using the namespacename :: member-name notation.Members of a namespace must be introduced using this notation:namespace namespace−name {// declaration and definitions}For example:namespace Parser {double expr(bool);double term(bool);double prim(bool);}// declarationdouble val = Parser::expr();// usedouble Parser::expr(bool b){// ...}// definitionWe cannot declare a new member of a namespace outside a namespace definition using the qualifiersyntax (§iso.7.3.1.2).
The idea is to catch errors such as misspellings and type mismatches, andalso to make it reasonably easy to find all names in a namespace declaration. For example:Section 14.2.1Explicit Qualificationvoid Parser::logical(bool);double Parser::trem(bool);double Parser::prim(int);393// error : no logical() in Parser// error : no trem() in Parser (misspelling)// error : Parser ::prim() takes a bool argument (wrong type)A namespace is a scope. The usual scope rules hold for namespaces.
Thus, ‘‘namespace’’ is a veryfundamental and relatively simple concept. The larger a program is, the more useful namespacesare to express logical separations of its parts. The global scope is a namespace and can be explicitly referred to using ::. For example:int f();int g(){int f;f();::f();}// global function// local variable; hides the global function// error: we can’t call an int// OK: call the global functionClasses are namespaces (§16.2).14.2.2 using-DeclarationsWhen a name is frequently used outside its namespace, it can be a bother to repeatedly qualify itwith its namespace name.
Consider:#include<string>#include<vector>#include<sstream>std::vector<std::string> split(const std::string& s)// split s into its whitespace-separated substrings{std::vector<std::string> res;std::istringstream iss(s);for (std::string buf; iss>>buf;)res.push_back(buf);return res;}The repeated qualification std is tedious and distracting. In particular, we repeat std::string fourtimes in this small example. To alleviate that we can use a using-declaration to say that in this codestring means std::string:using std::string;// use ‘‘string’’ to mean ‘‘std::string’’std::vector<string> split(const string& s)// split s into its whitespace-separated substrings{std::vector<string> res;std::istringstream iss(s);394NamespacesChapter 14for (string buf; iss>>buf;)res.push_back(buf);return res;}A using-declaration introduces a synonym into a scope. It is usually a good idea to keep local synonyms as local as possible to avoid confusion.When used for an overloaded name, a using-declaration applies to all the overloaded versions.For example:namespace N {void f(int);void f(string);};void g(){using N::f;f(789);f("Bruce");}// N::f(int)// N::f(string)For the use of using-declarations within class hierarchies, see §20.3.5.14.2.3 using-DirectivesIn the split() example (§14.2.2), we still had three uses of std:: left after introducing a synonym forstd::string.
Often, we like to use every name from a namespace without qualification. That can beachieved by providing a using-declaration for each name from the namespace, but that’s tediousand requires extra work each time a new name is added to or removed from the namespace. Alternatively, we can use a using-directive to request that every name from a namespace be accessible inour scope without qualification. For example:using namespace std;// make every name from std accessiblevector<string> split(const string& s)// split s into its whitespace-separated substrings{vector<string> res;istringstream iss(s);for (string buf; iss>>buf;)res.push_back(buf);return res;}A using-directive makes names from a namespace available almost as if they had been declaredoutside their namespace (see also §14.4). Using a using-directive to make names from a frequentlyused and well-known library available without qualification is a popular technique for simplifyingcode.
This is the technique used to access standard-library facilities throughout this book. Thestandard-library facilities are defined in namespace std.Section 14.2.3using-Directives395Within a function, a using-directive can be safely used as a notational convenience, but careshould be taken with global using-directives because overuse can lead to exactly the name clashesthat namespaces were introduced to avoid. For example:namespace Graph_lib {class Shape { /* ... */ };class Line : Shape { /* ... */ };class Poly_line: Shape { /* ... */ };class Text : Shape { /* ...
*/ };// connected sequence of lines// text labelShape operator+(const Shape&, const Shape&);Graph_reader open(const char∗);// compose// open file of Shapes}namespace Text_lib {class Glyph { /* ... */ };class Word { /* ... */ };class Line { /* ... */ };class Text { /* ... */ };// sequence of Glyphs// sequence of Words// sequence of LinesFile∗ open(const char∗); // open text fileWord operator+(const Line&, const Line&);// concatenate}using namespace Graph_lib;using namespace Text_lib;Glyph gl;vector<Shape∗> vs;// Text_lib::Glyph// Graph_lib::ShapeSo far, so good.
In particular, we can use names that do not clash, such as Glyph and Shape. However, name clashes now occur as soon as we use one of the names that clash – exactly as if we hadnot used namespaces. For example:Text txt;File∗ fp = open("my_precious_data");// error : ambiguous// error : ambiguousConsequently, we must be careful with using-directives in the global scope. In particular, don’tplace a using-directive in the global scope in a header file except in very specialized circumstances(e.g., to aid transition) because you never know where a header might be #included.14.2.4 Argument-Dependent LookupA function taking an argument of user-defined type X is more often than not defined in the samenamespace as X.
Consequently, if a function isn’t found in the context of its use, we look in thenamespaces of its arguments. For example:396NamespacesChapter 14namespace Chrono {class Date { /* ... */ };bool operator==(const Date&, const std::string&);std::string format(const Date&);// ...// make string representation}void f(Chrono::Date d, int i){std::string s = format(d);std::string t = format(i);}// Chrono::format()// error : no format() in scopeThis lookup rule (called argument-dependent lookup or simply ADL) saves the programmer a lot oftyping compared to using explicit qualification, yet it doesn’t pollute the namespace the way ausing-directive (§14.2.3) can. It is especially useful for operator operands (§18.2.5) and templatearguments (§26.3.5), where explicit qualification can be quite cumbersome.Note that the namespace itself needs to be in scope and the function must be declared before itcan be found and used.Naturally, a function can take arguments from more than one namespace.
For example:void f(Chrono::Date d, std::string s){if (d == s) {// ...}else if (d == "August 4, 1914") {// ...}}In such cases, we look for the function in the scope of the call (as ever) and in the namespaces ofevery argument (including each argument’s class and base classes) and do the usual overload resolution (§12.3) of all functions we find. In particular, for the call d==s, we look for operator== in thescope surrounding f(), in the std namespace (where == is defined for string), and in the Chrononamespace. There is a std::operator==(), but it doesn’t take a Date argument, so we useChrono::operator==(), which does. See also §18.2.5.When a class member invokes a named function, other members of the same class and its baseclasses are preferred over functions potentially found based on the argument types (operators follow a different rule; §18.2.1, §18.2.5).