Question

In C, if I use #include "someFile.h", the preprocessor does a textual import, meaning that the contents of someFile.h are "copy and pasted" onto the #include line. In C++, there is the using directive. Does this work in a similar way to the #include, ie: a textual import of the namespace?

using namespace std; // are the contents of std physically inserted on this line?

If this is not the case, then how is the using directive implemented`.

Était-ce utile?

La solution

The using namespace X will simply tell the compiler "when looking to find a name, look in X as well as the current namespace". It does not "import" anything. There are a lot of different ways you could actually implement this in a compiler, but the effect is "all the symbols in X appear as if they are available in the current namespace".

Or put another way, it would appear as if the compiler adds X:: in front of symbols when searching for symbols (as well as searching for the name itself without namespace).

[It gets rather complicated, and I generally avoid it, if you have a symbol X::a and local value a, or you use using namespace Y as well, and there is a further symbol Y::a. I'm sure the C++ standard DOES say which is used, but it's VERY easy to confuse yourself and others by using such constructs.]

In general, I use explicit namespace qualifiers on "everything", so I rarely use using namespace ... at all in my own code.

Autres conseils

No, it does not. It means that you can, from this line on, use classes and functions from std namespace without std:: prefix. It's not an alternative to #include. Sadly, #include is still here in C++.

Example:

#include <iostream>

int main() {
  std::cout << "Hello "; // No `std::` would give compile error!
  using namespace std;
  cout << "world!\n"; // Now it's okay to use just `cout`.
  return 0;
}

Nothing is "imported" into the file by a using directive. All it does is to provide shorter ways to write symbols that already exist in a namespace. For example, the following will generally not compile if it is the first two lines of a file:

#include <string>
static const string s("123");

The <string> header defines std::string, but string is not the same thing. You haven't defined string as a type, so this is an error.

The next code snippet (at the top of a different file) will compile, because when you write using namespace std, you are telling the compiler that string is an acceptable way to write std::string:

#include <string>
using namespace std;
static const string s("123");

But the following will not generally compile when it appears at the top of a file:

using namespace std;
static const string s("123");

and neither will this:

using namespace std;
static const std::string s("123");

That's because using namespace doesn't actually define any new symbols; it required some other code (such as the code found in the <string> header) to define those symbols.

By the way, many people will wisely tell you not to write using namespace std in any code. You can program very well in C++ without ever writing using namespace for any namespace. But that is the topic of another question that is answered at Why is "using namespace std" considered bad practice?

No, #include still works exactly the same in C++.

To understand using, you first need to understand namespaces. These are a way of avoiding the symbol conflicts which happen in large C projects, where it becomes hard to guarantee, for example, that two third-party libraries don't define functions with the same name. In principle everyone can choose a unique prefix, but I've encountered genuine problems with non-static C linker symbols in real projects (I'm looking at you, Oracle).

So, namespace allows you to group things, including whole libraries, including the standard library. It both avoids linker conflicts, and avoids ambiguity about which version of a function you're getting.

For example, let's create a geometry library:

// geo.hpp
struct vector;
struct matrix;

int transform(matrix const &m, vector &v); // v -> m . v

and use some STL headers too:

// vector
template <typename T, typename Alloc = std::allocator<T>> vector;

// algorithm
template <typename Input, typename Output, typename Unary>
void transform(Input, Input, Output, Unary);

But now, if we use all three headers in the same program, we have two types called vector, two functions called transform (ok, one function and a function template), and it's hard to be sure the compiler gets the right one each time. Further, it's hard to tell the compiler which we want if it can't guess.

So, we fix all our headers to put their symbols in namespaces:

// geo.hpp
namespace geo {
    struct vector;
    struct matrix;

    int transform(matrix const &m, vector &v); // v -> m . v
}

and use some STL headers too:

// vector
namespace std {
    template <typename T, typename Alloc = std::allocator<T>> vector;
}

// algorithm
namespace std {
    template <typename Input, typename Output, typename Unary>
    void transform(Input, Input, Output, Unary);
}

and our program can distinguish them easily:

#include "geo.hpp"
#include <algorithm>
#include <vector>

geo::vector origin = {0,0,0};

typedef std::vector<geo::vector> path;

void transform_path(geo::matrix const &m, path &p) {
    std::transform(p.begin(), p.end(), p.begin(),
                   [&m](geo::vector &v) -> void { geo::transform(m,v); }
                   );
}

Now that you understand namespaces, you can also see that names can get pretty long. So, to save typing out the fully-qualified name everywhere, the using directive allows you to inject individual names, or a whole namespace, into the current scope.

For example, we could replace the lambda expression in transform_path like so:

#include <functional>
void transform_path(geo::matrix const &m, path &p) {
    using std::transform;              // one function
    using namespace std::placeholders; // an entire (nested) namespace

    transform(p.begin(), p.end(), p.begin(),
              std::bind(geo::transform, m, _1));
    //                                 this ^ came from the
    //                                 placeholders namespace
    // ^ note we don't have to qualify std::transform any more
}

and that only affects those symbols inside the scope of that function. If another function chooses to inject the geo::transform instead, we don't get the conflict back.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top