为什么`std::ostream& operator<<` 覆盖必须在 C++ 的“全局”范围内声明?

Pib*_*bmy 2 c++

描述

我重写<< operatorstd::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;
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
Run Code Online (Sandbox Code Playgroud)

示例代码(不编译)

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

环境

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;
}
Run Code Online (Sandbox Code Playgroud)

Yks*_*nen 5

ADL再次罢工

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

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

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

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


一种可能的修复方法是您列出的,但它本身可能有问题,当编译器将使用aNamespace::TestEnum. 更喜欢在封装参数之一的命名空间中使用运算符重载。

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

 using ::operator<<;
Run Code Online (Sandbox Code Playgroud)