Channels ▼
RSS

Using Me


April 04: Using Me

Wendy called over the cubicle, "You shouldn't do that." As I had no clue what she was referring to, I made the most eloquent reply I could think of: "Huh?"

"Using directives. They're nasty," she said as she came into my cubicle.

"Nasty?!? What's wrong with them?"

"Well, they can introduce subtle bugs that are difficult to detect," she said. "Here, let me show you."

She scribbled on my white board:

// header A
namespace N
{
  void f( char * );
}
// header B
void f( void * );
// myfile.cpp
#include "headerA.h"
#include "headerB.h"
using namespace N;
int main()
{
  char * c;
  f( c );
}

"See, I might expect the call to go to ::f(void * ), but because of the using declaration, the compiler will match it with N::f( char * ) — not the function I wanted."

"Uh, well," I hemmed, "I guess that makes sense."

"Non-sense," the Guru snapped closed a thin, organge book [1]. "Prithee, child, what is the difference between the situation you have described, and this one," she took the marker from Wendy's hand and modified her code somewhat:

// header A
namespace N
{
  f( void * );
}

// header B
namespace N
{
  f( char * );
}

// main.cpp
#include "headerA.h"
#include "headerB.h"
int main()
{
  char * c;
  N::f( c );
}

"The problem you have described is not the fault of the using directive, but rather the ambiguity caused by the overloaded function."

"So, you're saying it's okay to put in using directives?" I asked warily.

The Guru paused for a moment. "A few years back, there was a great dissension and discord in the Usenet group comp.lang.c++ .moderated. This normally meditative group was rocked with discussion on the pros and cons of the using directive [2]. Prophets great and small argued on both sides of the matter. No clear consensus emerged from that debate; indeed, the moderators shut down the discussion because it was generating more heat than light, as the proverb goes.

"My own opinion and recommendation is that namespaces are designed to resolve name clashes, not to prevent them. As such, our coding guidelines tell us to write using directives or declarations to taste, with some restrictions. Thou shalt not allow a using directive or using declaration to appear in a header, nor shalt thou suffer a using directive or using declaration to appear before any #include statements in a file."

"But..." Wendy started

"Peace, child," the Guru interrupted, holding up her hand. "The debate covered almost all possible arguments, and there was no compelling, overwhelming technical reason to go one way or the other. Our coding guidelines neither require nor prohibit using directives, other than in or before header files."

"Ah, one question," I ventured. "Actually, two. What's the difference between a using declaration and a using directive, and why can't they be in headers?"

"A using declaration nominates a specific member of a namespace to appear in the current scope. You are likely most familiar with them in derived classes, to correct name hiding." She wrote quickly on the whiteboard:

class Base
{
public:
  void f( int );
};
class Derived : public Base
{
public:
  void f( double );
  using Base::f;
};

"They can also be used to introduce specific members of a namespace into the current scope,"

#include <iostream>
int main()
{
  using std::cout;
  cout << "Hello, world" << std::endl;
}

"Using directives, on the other hand, introduce the entire namespace into the current scope, and cannot be used at class scope,"

class Derived : public Base
{
  using namespace N; 
// Error, not allowed at class scope
};
// file.cpp
#include <iostream>
using namespace std;
int main()
{
  cout << "Hello, world" << endl;
}

"Now, apprentice, you were wondering why using directives are not allowed in headers. You do not have any control over, and should not make any assumptions about, what headers are included before or after yours. Therefore, you have no idea what names you will be dragging into scope, which may affect users of your headers.

"Consider this parable, wherein a third-party header has a using directive in it:"

// Yourheader.h
class Ambiguous
{
// ...whatever...
};

class UsesAmbiguous
{
   Ambiguous a;
// ... whatever ...
};

// thirdPartyHeader.h
namespace ThirdParty
{
  class Ambiguous
  {
    // ... whatever ...
  };
}
using namespace ThirdParty;

// YourSource.cpp
#include "thirdPartyHeader.h"
#include "Yourheader.h"
// etc...

"When you compile YourSource.cpp, all will be fine. However, if you change the order of #include statements:

// YourSource.cpp
#include "thirdPartyHeader.h"
#include "YourHeader.h"
// etc...

"You will now get a compiler error, when the compiler tries to parse the class UsesAmbiguous:

class UsesAmbiguous
{
  Ambiguous a; // Error, which one?
};

"The using directive has added all names from the namespace ThirdParty into the global namespace. There is no way to cancel a using directive. However, this can be rectified, as using directives only apply to unqualified lookup. A fully qualified name will suppress names introduced by the using directive. "

class UsesAmbiguous
{
  ::Ambiguous a;
};

"The proper solution, of course, is to prevent such clashes in the first place, by avoiding using declarations in headers. Violating third-party headers, though, is dangerous."

"Oh, ick, I see," I said. "Well, couldn't we just limit the problem with a using declaration?"

"Sadly, no," the Guru said, clearing off the whiteboard. "Indeed, the problem could be even worse, for now your program can change its meaning depending on the order in which your headers are included. Using declarations only nominate names that are seen at the point of declaration, not names that are introduced later. Consider this parable,"

// headerA
namespace ABigProject
{
  void f( int );
}

// headerB
namespace ABigProject
{
  void f( double );
}
using ABigProject::f;
// YourSource.cpp
#include "headerA.h"
#include "headerB.h"
int main()
{
  int i;
  f( i ); // OK, calls f( int );
  double d;
  f( d ); // OK, calls f( double );
}

"This first parable lives a pious life, and behaves as expected. Since the using declaration appears at the end of header B, the declaration nominates both versions of f for consideration in unqualified name lookup.

"Consider, however, the effect of changing the order of include statements:"

// BadSource.cpp
#include "headerB.h"
#include "headerA.h"
int main()
{
  int i;
  f( i ); // calls f( double )!!!
  double d;
  f( d ); // OK, calls f( double );
}

"When the compiler encounters the using declaration in header B, it only adds names from the namespace ABigProject that it has already seen. In this case, it has only seen f( double ). When the compiler encounters the unqualified call in main(), it does not consider looking at f(int), for that function still resides only in its namespace, not in the global namespace.

"If you are lucky, the ambiguity will be one that the compiler cannot resolve, and you will get a compile error. If you aren't lucky, well..." I shuddered at the thought of what might lie down that road.

"Okay, so I see why we can't use them in headers," I said at long last. "But what's wrong with putting them before #include statements?"

"I bet I know." I jumped a little at Kerry's voice. I hadn't noticed him joining the conversation. "Suppose header B behaved correctly," he said, erasing the using declaration from the whiteboard. "If you put a using declaration right after header B, it would be exactly the same as if the declaration was in the header, with the same results:

// BadSource.cpp
#include "headerB.h"
using ABigProject::f;
#include "headerA.h"

"Very good, young apprentice," the Guru approved. I felt a little chagrined at not having seen that for myself.

"One thing I don't get, though," Kerry said. "Is it okay to put a using directive or declaration in a namespace scope, like this?"

// some header
namespace N
{
  using namespace std;
  int count;
}

"After all, now you've limited the scope of the using directive, so you should be okay, right?"

"A good question, young apprentice, which deserves a good answer. Apprentice?" I realized she meant me. I kicked in the thought-afterburners.

"Well, it's obvious," I said, stalling for time. Then I remembered something the Guru had mentioned earlier. "You...uh...don't have any control over what other headers are going to be used. That's it! Yes, and if the source file is using another header with a conflicting name, you'll have problems. Here," I grabbed the marker and started writing:

// mysource.cpp
#include "someHeader.h"
#include <algorithm>
using namespace N;
int main()
{
  int x = count;
}

"Here, I meant to refer to N::count. But, namespace N pulls the entire std namespace into its scope. So, when I say count, the compiler doesn't know if I meant N::count or std::count from <algorithm>. Since there's no way to turn off the using declaration, I now have to qualify each mention of count — something I wouldn't have had to do, if the using directive wasn't in the header."

"Very good, apprentice. So, to sum up: Apply using directives or using declarations to taste — or not at all, if you prefer. Never put using declarations or directives in a header, not even at a namespace scope. One last point that must be mentioned. The most pious among us will put each project in its own namespace. This allows us at a later time to easily disambiguate any name clashes that may arise."

References

[1] Sutter, H. More Exceptional C++, Addison-Wesley, 2002. Items 39 and 40.

[2] http://tinyurl.com/36rd3 and http://www.google.ca/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&threadm=f0gV6.1%2422.962%40news.get2net.dk&rnum=65&prev=/groups%3Fq%3Dgroup:comp.lang.c%252B%252B.moderated%2Binsubject:%2522how%2Bto%2Bsay%2522%26hl%3Den%26lr%3D%26ie%3DUTF-8%26oe%3DUTF-8%26start%3D60%26sa%3DN%26filter%3D0.


Herb Sutter (http://www.gotw.ca/) is convener of the ISO C++ Standards committee, author of Exceptional C++ and More Exceptional C++, and Visual C++ architect for Microsoft. Jim Hyslop is a senior software designer for Leitch Technology International. He can be reached at jhyslop@ieee.orgM/a>.



Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video