【问题标题】:ADL with typedefs from another namespace带有来自另一个命名空间的 typedef 的 ADL
【发布时间】:2010-11-11 14:35:33
【问题描述】:

我有这样的事情:

#include <iostream>
namespace N
{
   typedef std::pair<int, double> MyPair;
   std::ostream& operator << (std::ostream& o, MyPair const & mypair)
   {
      ///
   }
}

int main()
{
    N::MyPair pr;
    std::cout << pr;
}

这自然是行不通的,因为 ADL 找不到 operator&lt;&lt;,因为 namespace N 没有与 MyPair 关联(很遗憾)。 Afaik 可能不会添加到命名空间 std,所以如果我选择在 std 中定义 operator &lt;&lt;,那将是非法的。那么......在这种情况下该怎么办?我不想明确限定operator &lt;&lt;,也不想写using namespace N。所以,问题是:

  1. 如何重构代码?
  2. 为什么 ADL 不关联 typedef 的命名空间?严重的原因?这会很好,例如在这种情况下。谢谢

【问题讨论】:

    标签: c++ typedef argument-dependent-lookup


    【解决方案1】:
    1. 您可以在命名空间 N 中创建自己的类型,可能继承自 std::pair。您可以添加“使用命名空间 N;”里面主要。前者更有可能有用。

    2. 因为类型是在另一个命名空间中定义的,不能在两个中定义。

    例子:

    namespace N { 
    struct MyPair : std::pair<int, double> {
      MyPair(int first, double second) : std::pair<int, double>(first, second) {}
      // add defaults if desired: first=0, second=0.0
      // with defaults, you may want to make the ctor explicit or leave implicit
    
      // also, if desired and you don't use two defaults above:
      MyPair() : std::pair<int, double>(0, 0.0) {}
    
      // in 0x, you can "import" the base's ctors with a using declaration
    };
    }
    

    如果用作 std::pair 并不重要,您可以放弃继承并重命名成员。当然,无论哪种情况,您都可以添加其他方法,但如果您保留继承,则可以使用“重命名方法”:

    int      & foo()       { return first; }
    int const& foo() const { return first; }
    double      & bar()       { return second; }
    double const& bar() const { return second; }
    

    【讨论】:

    • @Armen:什么看起来是人造的?
    • @Roger:为 ADL 创建一个新类
    • @Armen:嗯,不是。 :) 这样想,是什么让您的 MyPair 类型特别?它很特别,因为您需要特殊的输出格式。您如何将 MyPair 与其他配对区分开来?你为它创建一个特殊的类型。
    • 如果 MyPair 不是特殊的,那么它不需要自己的输出格式,你应该处理 op
    • @Armen:记住 typedef 是别名;以这种方式类似于命名空间别名和引用。
    【解决方案2】:

    我想不出typedef 名称不应参与 ADL 的原因。此外,它还定义了以下代码实现:

    #include <algorithm>
    #include <vector>
    
    namespace my {
    class A {};
    void for_each();
    } // my
    
    int main()
    {
        std::vector<my::A> v;
        for_each(v.begin(), v.end(), [...]);
    } 
    
    • 如果 std::vector&lt;T&gt;::iterator 是 std 命名空间中某事物的 typedef:std::for_each 将被调用
    • 如果 std::vector&lt;T&gt;::iteratormy::A * 的 typedef:编译器应该抱怨 my::for_each 不接受 3 个参数

    【讨论】:

      【解决方案3】:

      您的选择是:

      • 定义一个在其实现中使用 std::pair 而不是 typedef 的新类型
      • 为您的输出函数使用不同的名称
      • 在调用时明确限定您想要的函数
      • (也许)在命名空间 std 中专门化函数(我不确定 pair&lt;int,double&gt; 是否算作 UDT)

      这一切都源于 typedef 的主要优点和缺点:typedef 名称只是同义词。无论你把它放在哪个命名空间中,typedef 名称都是指关联类型,在该类型定义的任何命名空间中。这与 typedef 是不同的,它是可转换为关联类型/从关联类型转换的新类型。想象一下这种情况:

      class C{};
      typedef C id_t;
      void f(C);
      int f(id_t); // error: structurally equivalent to `int f(C);`
      

      这是无效的,因为 int 和 id_t 不是不同的类型。这延伸到 ADL:

      namespace A{
        class C{};
        void f(C);
        void g(C);
      }
      
      namespace B{
        typedef C id_t;
        int f(id_t); // structurally equivalent to `void f(C);`
      }
      
      B::id_t id; // completely equivalent to `A::C id;`
      int n = f(id); // error: A::f doesn't return int
      

      还有一个问题要问您:您认为以下内容应该无法编译吗?如果不是,名称查找应该如何解决:

      B::id_t id;
      g(id);
      

      【讨论】:

      • 嗯,是的,我相信理想情况下它不应该编译失败。名称查找将按如下方式进行:如果参数表达式是使用 typedefed-type 声明的标识符,则要查找 g 的关联命名空间必须包括声明 typedef 的命名空间。类似的东西......但是给它一些想法,我觉得这将是丑陋的,无用的和有问题的......
      【解决方案4】:

      我通过将相关符号拉入我想使用它们的命名空间来解决这个问题:

      #include <iostream>
      
      namespace N
      {
         typedef std::pair<int, double> MyPair;
         std::ostream& operator << (std::ostream& o, MyPair const & mypair)
         {
            ///
         }
      }
      
      using N::operator <<; // now it should compile
      
      int main()
      {
          N::MyPair pr;
          std::cout << pr;
      }
      

      【讨论】:

        【解决方案5】:

        你可以使用强类型定义:

        #include<boost/strong_typedef.hpp>    
        #include<iostream>
        
        namespace N
        {
        // typedef std::pair<int, double> MyPair;
           typedef std::pair<int, double> pair_int_double; 
           BOOST_STRONG_TYPEDEF(pair_int_double, MyPair);
        
           std::ostream& operator << (std::ostream& o, MyPair const & mypair)
           {
              return o;
           }
        }
        
        int main(){
            N::MyPair pr;
            std::cout << pr;
        }
        

        (仍然需要额外的 typedef 以避免宏中的额外逗号。)

        【讨论】:

          【解决方案6】:

          如果您想要输出特定的数据类型,您始终可以定义自己的类而不是使用std::pair

          struct myPair
          {
            int first;
            double second;
          };
          

          【讨论】:

          • @Let_Me_Be:这会有所帮助,因为 ADL 会在命名空间 N 中找到运算符
          • 您不妨继承,特别是如果您要使用相同的成员名称。 :) 但是,您只能在 0x 中为 ctors 添加 using 声明。
          • @Roger:顺便问一下,0x 是否允许添加到命名空间 std?这是一个非常烦人的限制
          • @Armen:据我所知,您的限制与 03 相同:只有在包含 UDT 时才能进行专业化。它在 03 中不是那么有用,因为您不能部分专门化函数,但我不相信 pair 无论如何都算作 UDT。
          【解决方案7】:

          允许向namespace::std 添加模板函数的特化,但是因为MyPair 中使用的类型都不是用户定义的,我不确定确定这样专业化是合法的。

          namespace std {
               template<>
               ostream& operator<<(ostream& os, const MyPair& p) { }
          }
          

          【讨论】:

            猜你喜欢
            • 2018-04-27
            • 1970-01-01
            • 1970-01-01
            • 2017-07-21
            • 2017-08-12
            • 1970-01-01
            • 2011-09-18
            • 2014-04-30
            • 1970-01-01
            相关资源
            最近更新 更多