2

在下面的代码中,有人可以解释为什么如果启用了由“ifdef TEST”分隔的代码,我定义的将枚举打印为字符串的 operator<< 函数没有被使用。在我看来,导致我的问题的代码应该与类 Container 中枚举的打印无关,特别是因为问题代码引用了不同的类(Container2)。

如果我使用 g++ filename.cpp 构建,则输出为:

Print for Container: mycolor is red

如果我使用 g++ -DTEST filename.cpp 构建,则输出为:

Print for Container: mycolor is 0

代码如下:#include

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

Simplified example:

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

The expression os << mycolor1 looks for a function named operator<<. The function will be searched for as a member function of std::ostream and (independently, additionally) via unqualified lookup, triggering ADL.

The unqualified lookup via ADL will find mynamespace::operator<<. The "pure" unqualified lookup (no ADL) will start from the scope of the function body of Print, which is the scope of the class Container (*) and traverse the surrounding scopes until a function named operator<< is found. Then it stops. Here, it stops at mynamespace as well: This is the first surrounding scope that has a function with that name. The global namespace is not searched.

You can make "pure" unqualified lookup find the global function e.g. by saying:

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

(*) You can think of it as

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

Here, it might be clearer that the first surrounding scope of os << mycolor1 that contains a function named operator<< is mynamespace.


IMHO, a good solution would be to put the operator<< for the enum in mynamespace as well:

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

This way, it can be found via 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 回答