2

I am puzzled by the following sentence found in the book Accelerated C++ by Koenig and Moo:

// median.h
#include <vector>
double median(std::vector<double>);

In this example, we use the qualified name for std::vector because we have no way of knowing how the user of our function wants to refer to std::vector. Users of our code might not want a using-declaration for vector. If we write one in our header, then all programs that include our header get a using std::vector declaration, regardless of whether they wanted it. Header files should use fully qualified names rather than using-declarations.

I don't think I understand this passage. If we had a using std::vector declaration in our header, and the whole program including the header gets the same declaration, this could be a problem only if the user is redefining the vector class, isn't it? But then our median function would still require a vector from the standard library, so if the user wanted to use median on a newly defined vector, it would give an error at compile time.

J. D.
  • 279
  • 1
  • 9
  • 2
    Not "redefining the vector class", defining a different class, in a different namespace, with the name "vector". If we didn't care, the standard could have put vector in the global namespace directly. – Marc Glisse Feb 23 '19 at 23:32
  • 2
    related: https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice – 463035818_is_not_an_ai Feb 23 '19 at 23:32
  • 1
    If you had `using std::vector` and I would include your header, I couldn't do `stop using std::vector` back in my code. – KamilCuk Feb 23 '19 at 23:35

2 Answers2

6

If the header file was:

#include <vector>
using std::vector;
double median(vector<double>);

This would tell all compilation units (e.g. cpp files) which include median.h that the function median accepts std::vector and at the same time makes std::vector accessible with unqualified lookup vector in those compilation units.

If a compilation unit had access to another unqualified vector, there would be a name conflict and a build error. The function median would never receive a type that is not std::vector even if the name of that type was also vector.

It's a good idea to avoid using declarations in header files because it's a cause for creating name conflicts.

Tugrul Ates
  • 9,451
  • 2
  • 33
  • 59
4

To understand the words, it is necessary to understand what a using directive like using namespace std does.

When it sees a name like vector the compiler, by default, looks for vector within the current context (i.e. in your example, that is in the "global namespace" or, equivalently in ::). If it doesn't find a candidate match (i.e. something named vector) in that search it issues a diagnostic, and compilation ceases.

The using namespace std causes the compiler to look in namespace std for matching names. If it finds an unambiguous match then that name is used.

The problem comes in if there is another using directive, such as using namespace foo in effect. For example;

 using namespace std;
 #include "foo.h"
 using namespace foo;
 #include "median.h"

Now consider what happens if foo.h specifies a templated something named vector within namespace foo. Assume that median.h has

double median(vector<double>);

Because of the two using directives, the compiler looks in both std and foo for a name vector. If it finds a match in both namespaces, then it has two candidate matching names, foo::vector and std::vector.

If both of those names are equally good matches to vector, then the code has a diagnosable error due to ambiguity. The compilation will fail. There is no rule that makes the compiler consider a match in namespace std is better than one in namespace foo, or vice versa.

The next problem is that there is no way to undo the effect of a using directive (e.g. with other using directives or declarations). The only way to get the code to compile in such a case is to change the declaration of median() to

double median(std::vector<double>);

Rather than going through all of that, Koenig and Moo are advocating using the full name std::vector<double> from the start, rather than messing with using directives in the header. Doing that allows other programmers, who use the header file, to employ using directives or declarations if they choose, but does not require them to do so.

Note: the above is an over-simplification. There are quite a few detailed rules that govern how a compiler finds candidates names, and then determines how good a match they are.

Peter
  • 35,646
  • 4
  • 32
  • 74