【问题标题】:operator << argument-dependent lookup does not look in global namespace运算符 << 依赖于参数的查找不在全局命名空间中查找
【发布时间】:2018-10-19 19:28:31
【问题描述】:

在以下场景中,在 function() 内部,ss &lt;&lt; bb 行出现错误:

binary '

我的 ADL 理解是它将查看当前命名空间(即 AppNamespace::InnerNamespace),由于没有找到 operator &lt;&lt;,它会查看参数的命名空间,因为 CommonType 在全局命名空间中我希望能够找到 CommonTypes.h 中定义的operator &lt;&lt;。 显然我的理解是错误的。谁能弄清楚这应该如何工作?

Main.cpp

namespace AppNamespace
{

    typedef std::vector<std::string> OtherType;

    std::ostream& operator << (std::ostream& os, const OtherType& ot)
    {
        for (auto& el : ot)
        {
            os << el;
        }
        return os;
    }

    namespace InnerNamespace
    {
        void function()
        {
            CommonType bb;
            std::stringstream ss;
            ss << bb;
        }
    }
}


int main()
{
    AppNamespace::InnerNamespace::function();
    return 0;
}

CommonTypes.h:

    #pragma once

#include <vector>

typedef std::vector<uint8_t> CommonType;

std::ostream& operator << (std::ostream& os, const CommonType& bb)
{
        for (auto& el : bb)
        {
            os << el;
        }
        return os;
}

【问题讨论】:

  • 即使没有别名问题,你对ADL的理解也是错误的。参数命名空间的重载被认为是甚至存在于全局命名空间中。我们首先构建要考虑的命名空间集。

标签: c++ namespaces operator-overloading argument-dependent-lookup


【解决方案1】:

这里的问题是 ADL 查找将在 std 命名空间中执行,而声明 CommonType 的命名空间不相关,因此为了使这项工作您需要将运算符

namespace std
{

ostream& operator << (ostream& os, const CommonType& bb)
{
    for (auto& el : bb)
    {
        os << el;
    }
    return os;
}

}

【讨论】:

  • 不是添加到命名空间 std UB 吗? en.cppreference.com/w/cpp/language/extending_std
  • @PaulFloyd 没必要,有时这是使用标准库的正确方式,例如std::is_error_code_enum 或std::default_delete 专业化。
  • 链接中提到的,允许部分特化,不允许声明和完全特化。
  • @PaulFloyd 那篇文章有些不完整。 std::is_error_code_enum 是允许完全专业化的示例。对于operator &lt;&lt;,我们不能将它放在其他任何地方,因为ADL 查找将在std 命名空间中执行,因为两个参数都来自std 命名空间。见similar question - Where should I define operator >> for my specialization of std::pair
  • "没有添加到命名空间 std UB 吗?" - 通常,是的。但是该标准明确允许将模板特化添加到命名空间 std(据我所知,这是您唯一可以添加的内容)。
【解决方案2】:

我的 ADL 理解是它将查看当前命名空间(即 AppNamespace::InnerNamespace),由于没有找到 namespace 并且因为 CommonType 在全局命名空间我希望找到 CommonTypes.h 中定义的

误解是CommonType是一个新类型。它不是一种新类型。它只是一个别名。

您可以使用各种方法解决函数查找问题。

  1. 在全局命名空间中定义两个 operator&lt;&lt; 函数。

    #include <string>
    #include <sstream>
    #include <iostream>
    #include <vector>
    #include <cstdint>
    
    // Put everything from CommonTypes.h here to simplify things.
    typedef std::vector<uint8_t> CommonType;
    
    std::ostream& operator<<(std::ostream& os, CommonType const& bb)
    {
       for (auto& el : bb)
       {
          os << el;
       }
       return os;
    }
    
    namespace AppNamespace
    {
        typedef std::vector<std::string> OtherType;
    }
    
    std::ostream& operator << (std::ostream& os, const AppNamespace::OtherType& ot)
    {
       for (auto& el : ot)
       {
          os << el;
       }
       return os;
    }
    
    namespace AppNamespace
    {
       namespace InnerNamespace
       {
          void function()
          {
             CommonType bb;
             std::stringstream ss;
             ss << bb;
          }
       }
    }
    
    
    int main()
    {
       AppNamespace::InnerNamespace::function();
       return 0;
    }
    
  2. 在 AppNamespace 命名空间中定义两个 operator&lt;&lt; 函数。

    #include <iostream>
    #include <vector>
    #include <cstdint>
    
    // Put everything from CommonTypes.h here to simplify things.
    typedef std::vector<uint8_t> CommonType;
    
    namespace AppNamespace
    {
       std::ostream& operator<<(std::ostream& os, CommonType const& bb)
       {
          for (auto& el : bb)
          {
             os << el;
          }
          return os;
       }
    
       typedef std::vector<std::string> OtherType;
       std::ostream& operator << (std::ostream& os, const OtherType& ot)
       {
          for (auto& el : ot)
          {
             os << el;
          }
          return os;
       }
    
       namespace InnerNamespace
       {
          void function()
          {
             CommonType bb;
             std::stringstream ss;
             ss << bb;
          }
       }
    }
    
    
    int main()
    {
       AppNamespace::InnerNamespace::function();
       return 0;
    }
    
  3. 在另一个命名空间中定义std::ostream&amp; operator&lt;&lt;(std::ostream&amp; os, CommonType const&amp; bb),并将该函数显式引入AppNamespace 的范围内。

    #include <string>
    #include <sstream>
    #include <iostream>
    #include <vector>
    #include <cstdint>
    
    // Put everything from CommonTypes.h here to simplify things.
    typedef std::vector<uint8_t> CommonType;
    
    namespace CommonNamespace
    {
       std::ostream& operator<<(std::ostream& os, CommonType const& bb)
       {
          for (auto& el : bb)
          {
             os << el;
          }
          return os;
       }
    }
    
    namespace AppNamespace
    {
       // Bring the operator<< functions in CommonNamespace into the scope of
       // this namespace.
       using CommonNamespace::operator<<;
    
       typedef std::vector<std::string> OtherType;
       std::ostream& operator << (std::ostream& os, const OtherType& ot)
       {
          for (auto& el : ot)
          {
             os << el;
          }
          return os;
       }
    
       namespace InnerNamespace
       {
          void function()
          {
             CommonType bb;
             std::stringstream ss;
             ss << bb;
          }
       }
    }
    
    
    int main()
    {
       AppNamespace::InnerNamespace::function();
       return 0;
    }
    

【讨论】:

  • 感谢三个解决方案!
【解决方案3】:

如果您不想按照 VTT 的建议将运算符移动到 std 命名空间中,您可以指定它位于全局命名空间中:

(::operator <<)(ss, bb);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-14
    • 2015-09-24
    • 1970-01-01
    • 2011-09-03
    • 1970-01-01
    相关资源
    最近更新 更多