Why is "using namespace X;" not allowed at class/struct level?
Asked Answered
D

6

115
class C {
  using namespace std;  // error
};
namespace N {
  using namespace std; // ok
}
int main () {
  using namespace std; // ok
}

I want to know the motivation behind it.

Decaliter answered 13/6, 2011 at 4:56 Comment(3)
@pst: C# does not have anything like using namespace. C# allows something similar, but at file scope only. C++'s using namespace allows you to incorporate one namespace into another.Desirae
Duplicate of this question?Zebra
I think this question is even more relevant in light of allowing using string = std::string; at class scope. All the resolution confusions that using namespace std; creates is is already an issue, if the class author is willing to be explicit about it.Collado
D
41

I don't know exactly, but my guess is that allowing this at class scope could cause confusion:

namespace Hello
{
    typedef int World;
}

class Blah
{
    using namespace Hello;
public:
    World DoSomething();
}

//Should this be just World or Hello::World ?
World Blah::DoSomething()
{
    //Is the using namespace valid in here?
}

Since there is no obvious way of doing this, the standard just says you can't.

Now, the reason this is less confusing when we're talking namespace scopes:

namespace Hello
{
    typedef int World;
}

namespace Other
{
    using namespace Hello;
    World DoSomething();
}

//We are outside of any namespace, so we have to fully qualify everything. Therefore either of these are correct:

//Hello was imported into Other, so everything that was in Hello is also in Other. Therefore this is okay:
Other::World Other::DoSomething()
{
    //We're outside of a namespace; obviously the using namespace doesn't apply here.
    //EDIT: Apparently I was wrong about that... see comments. 
}

//The original type was Hello::World, so this is okay too.
Hello::World Other::DoSomething()
{
    //Ditto
}

namespace Other
{
    //namespace Hello has been imported into Other, and we are inside Other, so therefore we never need to qualify anything from Hello.
    //Therefore this is unambiguiously right
    World DoSomething()
    {
        //We're inside the namespace, obviously the using namespace does apply here.
    }
}
Desirae answered 13/6, 2011 at 5:9 Comment(14)
+1, I thought of this reason, but then same thing is applicable for using namespace Hello; inside other namespace also (and declaring extern function inside it).Decaliter
@iammilind: But the rules for that are much less confusing -- the one namespace is imported into the other namespace. Give me a sec to edit...Desirae
I don't think its confusing. C++ isn't about guesswork. If it were allowed, then the C++ ISO committee would have specified in the language specification. Then you wouldn't say its confusing. Otherwise one might say even this is confusing : ideone.com/npOeD ... but then the rule for such coding is specified in the spec.Dressmaker
@Nawaz: Hmm.. that's the opposite behavior compared to what I would expect.Desirae
@Billy: But then the rule is specified in the spec. Once you read the rule, then nothing is confusing.Dressmaker
@Nawaz: I would disagree with that. The spec is designed to do what people generally expect as reasonable behavior. That is, the spec is designed around people, not the other way around.Desirae
@Billy: Which people? What might confusing to you, may not be confusing for me. And once the rules are laid down in whitepaper, nothing is confusing then. As I said, C++ isn't about guesswork.Dressmaker
@Nawaz: Most users of the language. I never said C++ was about guesswork. I am saying that when the spec is designed, it's designed with what behavior most programmers will expect ahead of time. And the rules on paper often are confusing -- the standard attempts to be unambiguous but it does not always succeed.Desirae
On the first example, it should be: Hello::World Blah::DoSomething() or Blah::World Blah::DoSomething() (if it was allowed), the return type of a member function definition is not considered to be in the scope of the class in the language, so it has to be qualified. Consider the valid example of replacing the using with a typedef Hello::World World; at class scope. So there should be no surprises there.Strobotron
(Not relevant to the discussion at hand, but your typedef is backwards: it should be typedef int World;)Impartible
@Adam: Thanks! Gah! Wish it worked in the same order as assignmentsDesirae
If it were allowed, I believe it would be applied on a lexical scope level. I think this is the "obvious" solution with virtually no suprises.Lighthearted
+1 Thomas Eding, also this exact sort of ambiguity can already happen when importing a namespace into a function or alternate namespace. C++ has rules for dealing with that -- so why not within a class scope?Washcloth
It is same like typedefing inside a class. If you typedef int MyNumber inside class MyClass, then you have to write MyClass::MyNumber MyClass::getNumber() {...}. Still typedef inside a class is allowed and namespace not? Illogical.Feints
I
14

Because the C++ standard explicitly forbids it. From C++03 §7.3.4 [namespace.udir]:

using-directive:
    using namespace ::opt nested-name-specifieropt namespace-name ;

A using-directive shall not appear in class scope, but may appear in namespace scope or in block scope. [Note: when looking up a namespace-name in a using-directive, only namespace names are considered, see 3.4.6. ]

Why does the C++ standard forbid it? I don't know, ask a member of the ISO committee that approved the language standard.

Impartible answered 13/6, 2011 at 5:5 Comment(5)
Yet another technically correct but useless answer; the worst kind. 1) more people than just the committee know the answer. 2) committee members participate in SO 3) if you don't know the answer (given the spirit of the question) why answer at all?Caraway
@Catskul: it's not a useless answer. It's very useful to know that the standard explicitly addresses this and forbids it. It's also ironic that the most upvoted answer starts with "I don't know exactly". Also, the "standard forbids it" is not the same as "it isn't allowed because the compiler doesn't allow it", because the latter case would not answer follow-up questions like: is it a problem with my compiler? is the compiler not standard-compliant? is it a side effect of some other things that I'm not aware of? etc.Saito
While it's not the "worst kind" of answer, it doesn't answer the question: why C++ forbids it. The OP already established that C++ forbids it.Brashear
@antonone The question acknowledges that this is currently forbidden and explicitly asks for motivation behind it. Hence this answer is inadequate and doesn't answer the question.Merry
@AndreySemashev The question acknowledges that "it's forbidden", but still it could be forbidden by the compiler implementation. This answer makes it clear that it's forbidden by the standard. This helps with focusing on further search of the more precise answer. Ambiguity of the question in this area makes me think that the problem is in the question, not in the answer.Saito
T
13

I believe that the rationale is that it would probably be confusing. Currently, while processing a class level identifier, lookup will first search in the class scope and then in the enclosing namespace. Allowing the using namespace at class level would have quite some side effects on how the lookup is now performed. In particular, it would have to be performed sometime between checking that particular class scope and checking the enclosing namespace. That is: 1) merge the class level and used namespace level lookups, 2) lookup the used namespace after the class scope but before any other class scope, 3) lookup the used namespace right before the enclosing namespace. 4) lookup merged with the enclosing namespace.

  1. This would make a big difference, where an identifier at class level would shadow any identifier in the enclosing namespace, but it would not shadow a used namespace. The effect would be strange, in that access to the used namespace from a class in a different namespace and from the same namespace would differ:

.

namespace A {
   void foo() {}
   struct B {
      struct foo {};
      void f() {
         foo();      // value initialize a A::B::foo object (current behavior)
      }
   };
}
struct C {
   using namespace A;
   struct foo {};
   void f() {
      foo();         // call A::foo
   }
};
  1. Lookup right after this class scope. This would have the strange effect of shadowing base classes' members. The current lookup does not mix class and namespace level lookups, and when performing class lookup it will go all the way to the base classes before considering the enclosing namespace. The behavior would be surprising in that it would not consider the namespace in a similar level to the enclosing namespace. Again, the used namespace would be prioritized over the enclosing namespace.

.

namespace A {
   void foo() {}
}
void bar() {}
struct base {
   void foo();
   void bar();
};
struct test : base {
   using namespace A;
   void f() {
      foo();           // A::foo()
      bar();           // base::bar()
   }
};
  1. Lookup right before the enclosing namespace. The problem with this approach is again that it would be surprising to many. Consider that the namespace is defined in a different translation unit, so that the following code cannot be seen all at once:

.

namespace A {
   void foo( int ) { std::cout << "int"; }
}
void foo( double ) { std::cout << "double"; }
struct test {
   using namespace A;
   void f() {
      foo( 5.0 );          // would print "int" if A is checked *before* the
                           // enclosing namespace
   }
};
  1. Merge with the enclosing namespace. This would have the exact same effect that applying the using declaration at the namespace level. It would not add any new value to that, but will on the other hand complicate lookup for compiler implementors. Namespace identifier lookup is now independent from where in the code the lookup is triggered. When inside a class, if lookup does not find the identifier at class scope it will fall back to namespace lookup, but that is exactly the same namespace lookup that is used in a function definition, there is no need to maintain new state. When the using declaration is found at namespace level, the contents of the used namespace are brought into that namespace for all lookups involving the namespace. If using namespace was allowed at class level, there would be different outcomes for namespace lookup of the exact same namespace depending on where the lookup was triggered from, and that would make the implementation of the lookup much more complex for no additional value.

Anyway, my recommendation is not to employ the using namespace declaration at all. It makes code simpler to reason with without having to keep all namespaces' contents in mind.

Taciturn answered 13/6, 2011 at 8:1 Comment(4)
I agree that using tends to create implicit oddities. But some libraries may get designed around the fact that using exists. By purposedly declaring things in deep nested long namespaces. E.g. glm does that, and uses multiple tricks to activate / present features when the client use using.Palocz
even right in the STL using namespace std::placeholders . c.f en.cppreference.com/w/cpp/utility/functional/bindPalocz
@v.oddou: namespace ph = std::placeholders;Strobotron
All your listed problems exist in a very similar fashion, when importing foreign symbols via using namespace A at top level, into any other namespace or into any function. i think, the main issue is, that namespace imported global variables and functions would look like fake static members of the class.Produce
D
7

This is probably disallowed because of openness vs closedness.

  • Classes and structs in C++ are always closed entities. They are defined in exactly one place (although you can split declaration and implementation).
  • namespaces can be opened, re-opened and extended arbitrarily often.

Importing namespaces into classes would lead to funny cases like this:

namespace Foo {}

struct Bar { using namespace Foo; };

namespace Foo {
using Baz = int; // I've just extended `Bar` with a type alias!
void baz(); // I've just extended `Bar` with what looks like a static function!
// etc.
}
Darling answered 5/7, 2020 at 11:8 Comment(8)
Or we could just NOT define class members with the imported names. Let this construct add namespace Foo to the search order for all code inside the type definition of struct Bar, much like putting that line in every inline member function body, except that it would also be active for brace-or-equal initializers, etc. But it would still expire at the closing brace, the same as using namespace inside a member function body. Now there unfortunately doesn't seem to be any way to use Koenig-with-fallback lookup in a brace-or-equal initializer without polluting the enclosing namespace.Tardigrade
I don't get why this is a problem. You can use using namespace in functions that are only defined once (I know inlining kinda bypasses this, but that is not important here), but you can't in classes.Jodoin
@HrvojeJurić because functions don't (re-)export any names. Classes/structs do.Darling
This answer's reasoning is incorrect. Structs/classes are defined once, and name lookup (for non-templates) is performed at the point of that definition. Any names that are introduced past that definition have no effect on the struct/class that is already defined.Merry
@AndreySemashev There's no well-defined notion of what is defined "before/after" the struct definition. Imagine multiple TUs each importing different headers together with a header defining the struct in question.Darling
@DanielSteck > There's no well-defined notion of what is defined "before/after" the struct definition. -- Right, but the point is once the struct is defined, it's defined, and the definition is only valid if the names it uses (e.g. types and functions) are already defined before. Once the struct is defined, any future definitions have no effect on it.Merry
> Imagine multiple TUs each importing different headers together with a header defining the struct in question. -- And? If the names used by the struct are equivalent across all TUs then the struct is defined the same way in each TU. Otherwise, it's an ODR violation, and the program is not conforming to the C++ standard.Merry
If anything, what you're arguing here is that using namepace Foo; inside a struct (assuming re-export of names) would be ill-defined in the first place. I don't really see the point of bike-shedding the specifics of such a hypothetical feature. The whole point of my answer was that such a feature would be confusing at best, and potentially ill defined at worst. Your comments are supporting this.Darling
C
5

I think it's a defect of the language. You may use workaround below. Keeping in mind this workaround, it is easy to suggest rules of names conflicts resolution for the case when the language will be changed.

namespace Hello
{
    typedef int World;
}
// surround the class (where we want to use namespace Hello)
// by auxiliary namespace (but don't use anonymous namespaces in h-files)
namespace Blah_namesp {
using namespace Hello;

class Blah
{
public:
    World DoSomething1();
    World DoSomething2();
    World DoSomething3();
};

World Blah::DoSomething1()
{
}

} // namespace Blah_namesp

// "extract" class from auxiliary namespace
using Blah_namesp::Blah;

Hello::World Blah::DoSomething2()
{
}
auto Blah::DoSomething3() -> World
{
}
Cuevas answered 20/12, 2019 at 9:2 Comment(3)
Can you please add some explanation ?Albuminous
Yes, I have added some commentsCuevas
This is good, but note that the name of the auxiliary namespace appears in error messages involving the class.Guff
D
-2

You can't use using namespace inside of a class, but what you can do is simply use #define and then #undef inside of the structure. It will act the exact same way as namespace a = b;

struct foo
{
#define new_namespace old_namespace
     
    void foo2()
    {
        new_namespace::do_something();
    }

#undef new_namespace 
};
Divulge answered 9/1, 2022 at 14:7 Comment(2)
While you can, if I was doing a code review, I would never let this pass.Frugal
I know it's not good practice but it's as good as it getsDivulge

© 2022 - 2024 — McMap. All rights reserved.