结构/类声明中的作用域 using 指令?

发布于 2024-10-06 12:08:45 字数 957 浏览 11 评论 0原文

我发现我的 C++ 头文件对于所有完全限定的类型(深达 4 个嵌套命名空间)都非常难以阅读(并且键入起来非常乏味)。这就是问题(所有答案都给出了实现它的混乱替代方案,但这不是问题):是否有强有力的理由反对在结构和类中引入作用域 using-directive在 C++ 语言中(虽然允许在函数中使用作用域声明)?

例如,

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... */);
};

由于 namespace 是一个关键字,我认为它足够独特,不会与作用域 using 声明(例如 Bar::MemberFunc)发生冲突。

编辑:仔细阅读问题--->我已经加粗了。提醒:我们讨论如何提高此处示例的可读性。建议如何在 C++ 语言中实现范围内的 using-directive(即通过添加关键字/构造等)并不是一个答案(如果您可以找到一种优雅的方法来使用现有的 C++ 来实现此目的)语言标准,那么这当然是一个答案)!

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)!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

最笨的告白 2024-10-13 12:08:45

有时我这样做是为了达到几乎相同的效果:

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;

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;
绝不服输 2024-10-13 12:08:45

鉴于类作用域中的 using 声明不是继承的,因此这是可行的。该名称仅在该类声明内或嵌套类的声明内有效。但我认为这有点超载了类的概念,而它的想法应该更大。

在 Java 和 Python 中,单个文件以特殊方式处理。您可以使用 import 声明将其他名称空间中的名称注入到文件中。这些名称(嗯,不完全是 Python,但这里解释起来太复杂)仅在该文件中可见。

对我来说,这种能力不依赖于类声明,而是赋予它自己的范围。如果有意义的话,这将允许注入的名称在多个类声明中使用,甚至在函数定义中使用。

这是我更喜欢的一个想法,因为它允许这些事情,同时仍然为您提供使用声明的类级别的好处:

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));

这类似于在函数中声明局部变量的块。但在这种情况下,您声明的范围非常有限,您将在其中更改名称查找规则。

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.

青衫负雪 2024-10-13 12:08:45

您可以在类声明中使用 typedef 来实现相同的效果

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

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

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);
};
不可一世的女人 2024-10-13 12:08:45

也许命名空间别名?

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

Maybe namespace alias?

namespace MyScope = System::Network::Win32::Sockets;
小…楫夜泊 2024-10-13 12:08:45

命名空间的明显好处是它们可以让您避免命名冲突。然而,通过将整个命名空间引入到类声明中,这种好处就消失了。 System 命名空间中的函数完全有可能与您自己的 Bar::MemberFunc 函数发生冲突。当您添加注释“与此不冲突”时,您甚至会在示例代码中注意到这一点。

您显然不想像这样将整个名称空间引入到您的类中:

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

并且您不能将像这样的范围更窄的 using 语句添加到类声明中。将它们直接放入类声明中是非法的。

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

可以做的是使用未命名的命名空间。因此,您的头文件将如下所示:

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*/);
};

这具有三个明显的优点。

  1. 整个命名空间的内容就不介绍了。这有助于更轻松地避免命名冲突。
  2. 在类声明之前,您有一个列表,其中列出了您的类正常工作所依赖的内容。
  3. 你的函数声明很干净。

如果我错了,请纠正我;我只是简单地测试了这一点。很快就写了一个例子,并编译了。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文