27

私の C++ヘッダー ファイルは、完全に修飾されたすべての型 (ネストされた名前空間が 4 つまで深くなる) では非常に読みにくい (そして、入力するのが非常に面倒) ことがわかりました。これが問題です (すべての答えは、それを実装するための厄介な代替手段を提供しますが、それは問題ではありません): C++ 言語の構造体とクラスにスコープ付き using-directive を導入することに対する強い理由はありますか?関数での宣言)?

例えば

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はキーワードであるため、Bar::MemberFunc.

編集: 質問を注意深く読んでください ---> 太字にしました。注意:ここでは、例の読みやすさを改善する方法については説明していません。C++ 言語でスコープ付き using-directive を実装する方法 (つまり、キーワード/構成要素などを追加する方法) を提案することは答えではありません(既存の C++ 言語標準を使用してこれを実装するエレガントな方法を見つけることができれば、それはもちろん答えになります)!

4

5 に答える 5

13

ほぼ同じ効果を得るために、時々これを行います。

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;
于 2010-12-06T05:17:19.347 に答える
11

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

これは、関数内でローカル変数のブロックを宣言することに似ています。ただし、この場合、名前検索ルールを変更する非常に限定されたスコープを宣言しています。

于 2010-12-06T05:24:42.610 に答える
0

たぶん名前空間エイリアス?

namespace MyScope = System::Network::Win32::Sockets;
于 2010-12-06T03:12:41.027 に答える
0

クラス宣言内で 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);
};
于 2014-03-04T09:09:58.447 に答える
-2

名前空間の明らかな利点は、名前の競合を回避できることです。ただし、名前空間全体をクラス宣言に導入すると、この利点は無効になります。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*/);
};

これには 3 つの明確な利点があります。

  1. 名前空間全体の内容は紹介されていません。これにより、名前の競合をより簡単に回避できます。
  2. クラス宣言の前に、クラスが正しく動作するために依存するもののリストがあります。
  3. 関数宣言はきれいです。

間違っている場合は訂正してください。これを簡単にテストしただけです。すぐに例を書き上げ、コンパイルしました。

于 2010-12-06T05:36:22.000 に答える