2

次のコードで、「ifdef TEST」で区切られたコードが有効になっている場合、列挙を文字列として出力するために定義した operator<< 関数が使用されない理由を説明してください。私の問題を引き起こしているコードは、特に問題のコードが別のクラス (Container2) を参照しているため、Container クラスの列挙型の出力とは無関係であるように思われます。

g++ filename.cpp でビルドすると、出力は次のようになります。

Print for Container: mycolor is red

g++ -DTEST filename.cpp でビルドすると、出力は次のようになります。

Print for Container: mycolor is 0

コードは次のとおりです。

namespace mynamespace
{
    enum color {red, blue};
}
namespace mynamespace
{
    class Container
    {
    public:
        mynamespace::color mycolor1;
        explicit Container() : mycolor1(mynamespace::red) {};
        std::ostream &Print(std::ostream& os) const;
    };
    class Container2
    {
    };
}
std::ostream & operator<<(std::ostream &os, const mynamespace::color &_color);

namespace mynamespace
{
#ifdef TEST
// If this is defined, the printing of the enum in Container does not use the operator<< function to output the enum as a string
std::ostream& operator<<(std::ostream &os, const Container2 &i);
#endif
}

int main()
{
    // Create a Container.  Default color is red
    mynamespace::Container *container = new mynamespace::Container;
    container->Print(std::cout);
}

std::ostream & mynamespace::Container::Print(std::ostream &os) const
{
    os << "Print for Container: mycolor is " << mycolor1 << std::endl;
    return os;
}
std::ostream& operator<<(std::ostream &os, const mynamespace::color &_color)
{
    switch(_color)
    {
        case mynamespace::red: os << "red"; break;
        case mynamespace::blue: os << "blue"; break;
        default: os << "unknown"; break;
    }
    return os;
}
4

2 に答える 2

4

簡単な例:

namespace mynamespace
{
    enum color {red, blue};

    class Container
    {
    public:
        mynamespace::color mycolor1;
        explicit Container() : mycolor1(mynamespace::red) {};
        std::ostream &Print(std::ostream& os) const;
    };
    class Container2
    {
    };

    std::ostream& operator<<(std::ostream &os, const Container2 &i);
}
std::ostream & operator<<(std::ostream &os, const mynamespace::color &_color);

std::ostream & mynamespace::Container::Print(std::ostream &os) const
{
    os << mycolor1 << std::endl;
    return os;
}

os << mycolor1は、 という名前の関数を探しoperator<<ます。std::ostream関数は、ADL をトリガーする非修飾ルックアップを介して (独立して、さらに) andのメンバー関数として検索されます。

ADL による非修飾ルックアップで が見つかりますmynamespace::operator<<。「純粋な」非修飾ルックアップ (ADL なし) はPrint、クラスContainer(*)のスコープである の関数本体のスコープから開始し、という名前の関数operator<<が見つかるまで周囲のスコープをトラバースします。その後停止します。ここでも停止しmynamespaceます。これは、その名前の関数を持つ最初の周囲のスコープです。グローバル名前空間は検索されません

たとえば、次のように言って、「純粋な」非修飾ルックアップでグローバル関数を見つけることができます。

std::ostream & mynamespace::Container::Print(std::ostream &os) const
{
    using ::operator<<;
    os << mycolor1 << std::endl;
    return os;
}

(*) と考えることができます。

namespace mynamespace { enum color {red, blue}; class Container2; }

std::ostream& operator<<(std::ostream &os, const mynamespace::color &_color);

namespace mynamespace
{
    std::ostream & operator<<(std::ostream &os, const Container2 &i);

    class Container
    {
    public:
        mynamespace::color mycolor1;
        explicit Container() : mycolor1(mynamespace::red) {};

        std::ostream &Print(std::ostream& os) const
        {
            os << mycolor1 << std::endl;
            return os;
        }
    };
};

os << mycolor1ここでは、 という名前の関数を含むの最初の周囲のスコープが であることがより明確になる場合がありoperator<<ますmynamespace


私見、良い解決策はoperator<<、列挙型mynamespaceも同様に入れることです:

namespace mynamespace
{
    enum color {red, blue};
    std::ostream & operator<<(std::ostream &os, const mynamespace::color &_color);
}

このようにして、ADL 経由で見つけることができます。

于 2013-11-05T16:21:33.943 に答える
1

コメントに記載されている問題は、名前空間関数が同じ名前のすべてのグローバル関数を隠していることです。using最も簡単な解決策は、次のように fwd 宣言にステートメントを入れることです。

namespace mynamespace
{
  using ::operator<<;
  std::ostream& operator<<(std::ostream &os, const Container2 &i);
}

これは、グローバル スコープ関数を置き換えているのではなく、オーバーロードしているだけであることをコンパイラに伝えます。

于 2013-11-05T16:29:43.513 に答える