c++ - 为什么必须在 C++ 的 `std::ostream& operator<<` 作用域中声明 'global' 覆盖?

标签 c++

描述

我正在覆盖 << operator对于 std::ostream以简化我的代码中的对象显示。 我在定义要显示的对象类型的地方使用不同的命名空间。

这导致我出现编译错误。我找到了解决方法。似乎必须在全局范围内声明覆盖,但我真的不明白为什么。有人可以解释导致错误的原因吗?

错误

main.cpp:22:38: error: no match for ‘operator<<’ (operand types are ‘std::basic_ostream’ and ‘std::vector’)
         std::cout <<"printVector = " << data << std::endl;
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~

示例代码(不要编译)

这是一个显示错误的虚拟示例。

#include <iostream>
#include <vector>

inline std::ostream&
operator<<(std::ostream& strm, std::vector<uint8_t>& buffer)
{
    return strm << "display std::vector<uint8_t>";
}
    
namespace aNamespace {
    enum TestEnum { a, b, c };
    
    inline std::ostream&
    operator<<(std::ostream& strm, TestEnum& value)
    {
        return strm << "display TestEnum";
    }
  
    static void printVector () 
    {
        std::vector<uint8_t> data {1, 12, 56, 241, 128};
        std::cout <<"printVector = " << data << std::endl;
    }
}


int main()
{
    aNamespace::printVector();
    return 0;
}

环境

C++11;海湾合作委员会; Linux

示例代码修复(已编辑)

这是为那些感兴趣的人准备的代码修复版本。

#include <iostream>
#include <vector>

inline std::ostream&
operator<<(std::ostream& strm, std::vector<uint8_t>& buffer)
{
    return strm << "display std::vector<uint8_t>";
}

namespace aNamespace {
    using ::operator<<;

    enum class TestEnum { a, b, c };
    
    inline std::ostream&
    operator<<(std::ostream& strm, TestEnum value)
    {
        return strm << "display TestEnum";
    }

    static void printVector () 
    {
        std::vector<uint8_t> data {1, 12, 56, 241, 128};
        std::cout <<"printVector = " << data << std::endl;
    }
}

int main()
{
    aNamespace::printVector();
    return 0;
}

最佳答案

ADL再次来袭

为了解决运算符重载问题,C++ 使用 Argument Dependent Lookup .简而言之,它将搜索任一参数的命名空间(在本例中为命名空间 std )和运算符调用的命名空间( aNamespace ),如果没有找到运算符重载,则沿层次结构向上搜索 em>.

命名空间 std不包含任何匹配的重载,但它包含其他不匹配的重载,因此不搜索父命名空间。

现在,如果命名空间 aNamespace不包含 operator << 的任何重载,搜索父 namespace (全局)并找到正确的重载,就像在您的“示例代码修复”示例中一样。
但是,如果它包含 operator << 的任何重载,即使是不匹配的,查找也不会考虑父命名空间并且无法找到正确的重载。

这就是为什么一些 C++ 专家提倡反对广泛使用命名空间,或者至少将其限制为每个项目的单个命名空间的原因之一(例如参见 Titus Winters 的 this article)。


一个可能的修复方法如您所列,但它本身可能有问题,当编译器将使用 aNamespace::TestEnum 查找重载时.更喜欢在封装其中一个参数的 namespace 中重载运算符。

更好的解决方案是将全局范围内的运算符显式添加到您的命名空间范围或函数范围:

 using ::operator<<;

关于c++ - 为什么必须在 C++ 的 `std::ostream& operator<<` 作用域中声明 'global' 覆盖?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67090717/

相关文章:

c++ - 在黑莓 10 中访问单例类方法

c++ - 此代码 O(R*C) 的最坏时间情况如何?

c++ - Qt 资源文件不编译就不会更新

C++ 关于 remove_if 的指针/引用

c++ - QLocalSocket/QDataStream应该如何读取才能避免死锁?

C++ - 字符串和 LPCWSTR 之间的转换

c++ - 使用 boost::mpi 的 mpi 中的消息大小是否有限制?

c++ - 使用 MFC 对象初始化 std::map 不编译

c++ - 具有非 void 函数的多线程

c++ - MFC 树控件通知被触发两次