开发者

Scoped using-directive within a struct/class declaration? [duplicate]

This question already has answers here: Why is "using namespace X;" not allowed at class/struct level? (6 answers) Closed 5 years ago.

I find that my C++ header files are quite hard to read (and really tedious to type) with all the fully-qualified types (which goes as deep as 4 nested namespaces). This is the question (all the answers give messy alternatives to implementing it, but that's not the question): Is there a strong reason against introducing scoped using-directive in structs and classes in the C++ language (while it's permissible to have scoped using-declaration in functions)?

e.g.

class Foo : public Bar
{
    using namespace System;
    using namespace System::Network;
    using namespace System::Network::Win32::Sockets;
    using Bar::MemberFunc; // no conflict with this

    // e.g. of how messy my header files are without scoped using-directive
    void FooBar(System::Network::Win32::Sockets::Handle handle, System::Network::Win32::Sockets::Error& error /*, more fully-qualified param declarations... */);
};

Since开发者_开发百科 namespace is a keyword, I would've thought it's distinct enough to cause no conflict with the scoped using declaration such as Bar::MemberFunc.

EDIT: Read the question carefully ---> I've bolded it. Reminder: we're not discussing how to improve readability of the example here. Suggesting how scoped using-directive could be implemented (i.e. by means of adding keywords / constructs etc.) in the C++ language is NOT an answer (if you could find an elegant way to implement this using existing C++ language standards, then it would of course be an answer)!


Sometimes I do this to achieve almost the same effect:

namespace detail {
    using namespace System;
    using namespace System::Network;
    using namespace System::Network::Win32::Sockets;

    class Foo : public Bar
    {
         void FooBar(Handle handle, Error& error);
    };
}
using detail::Foo;


Given that using declarations at class scope are not inherited, this could work. The name would only be valid inside that class declaration, or inside the declarations of nested classes. But I think it's sort of overloading the concept of a class with an idea that should be larger.

In Java and Python individual files are treated in a special way. You can have import declarations that inject names from other namespaces into the file. These names will (well, not exactly with Python, but it's too complicated to explain here) only be visible within that file.

To me that argues for this sort of ability not being tied to a class declaration, but given a scope of its own instead. This would allow injected names to be used in several class declarations if it made sense, or even in function definitions.

Here is an idea I prefer because it allows these things while still giving you the benefits of a class level using declaration:

using {
   // A 'using' block is a sort of way to fence names in.  The only names
   // that escape the confines of a using block are names that are not
   // aliases for other things, not even for things that don't have names
   // of their own.  These are things like the declarations for new
   // classes, enums, structs, global functions or global variables.
   // New, non-alias names will be treated as if they were declared in
   // the scope in which the 'using' block appeared.

   using namespace ::std;
   using ::mynamespace::mytype_t;
   namespace mn = ::mynamespace;
   using ::mynamespace::myfunc;

   class AClass {
     public:
      AClass(const string &st, mytype_t me) : st_(st), me_(me) {
         myfunc(&me_);
      }

     private:
      const string st_;
      mn::mytype_t me_;
   };
// The effects of all typedefs, using declarations, and namespace
// aliases that were introduced at the level of this block go away
// here.  typedefs and using declarations inside of nested classes
// or namespace declarations do not go away.
} // end using.

// Legal because AClass is treated as having been declared in this
// scope.
AClass a("Fred", ::mynamespace::mytype_t(5));

// Not legal, alias mn no longer exists.
AClass b("Fred", mn::mytype_t);

// Not legal, the unqualified name myfunc no longer exists.
AClass c("Fred", myfunc(::mynamespace::mytype_t(5));

This is analogous to declaring a block for local variables in a function. But in this case you are declaring a very limited scope in which you will be changing the name lookup rules.


Maybe namespace alias?

namespace MyScope = System::Network::Win32::Sockets;


You can use a typedef inside the class declaration to achieve the same

class Foo : public Bar
{
      typedef System::Network::Win32::Sockets::Handle Handle;
      typedef System::Network::Win32::Sockets::Error Error;

      void FooBar(Handle handle, Error& error);
};


The obvious benefit of namespaces is that they allow you to avoid naming conflicts. However, by introducing an entire namespace into a class declaration, this benefit is voided. It's entirely possible that a function in the System namespace could conflict with your own Bar::MemberFunc function. You even note this in your sample code when you added the comment "no conflict with this".

You obviously don't want to introduce entire namespaces into your class like this:

using namespace System;
using namespace System::Network;
using namespace System::Network::Win32::Sockets;

And you can't add more narrowly scoped using statements like this into a class declaration. Putting these directly into a class declaration is illegal.

using System::Network::Win32::Sockets::Handle;
using System::Network::Win32::Sockets::Error;

What you can do is use the unnamed namespace. So, your header file would look something like this:

namespace {
    using System::Network::Win32::Sockets::Handle;
    using System::Network::Win32::Sockets::Error;
}

class Foo : public Bar
{
    using Bar::MemberFunc;

    // clean!
    void FooBar(Handle handle, Error& error /*, more declarations*/);
};

This has three distinct advantages.

  1. The contents of entire namespaces are not introduced. This helps to more easily avoid naming conflicts.
  2. You have a list, before your class declaration, of what your class depends upon to work correctly.
  3. Your function declarations are clean.

Please correct me if I'm wrong; I only briefly tested this. Quickly wrote up an example, and it compiled.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜