使用另一个命名空间中的typedef进行ADL

时间:2010-11-11 14:35:33

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

我有这样的事情:

#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<<,因为namespace NMyPair无关(不幸的是)。 Afaik可能不会添加到命名空间std,所以如果我选择在std中定义operator <<有点非法。那么......在这种情况下该怎么办?我不想明确限定operator <<,也不想写using namespace N。所以,问题是:

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

7 个答案:

答案 0 :(得分:4)

  1. 您可以在命名空间N中创建自己的类型,可能继承自std :: pair。你可以添加“using namespace N;”在主要内部。前者更有可能有用。

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

  3. 示例:

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

答案 1 :(得分:3)

我想不出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<T>::iterator是std名称空间中的某个类型的typedef:std::for_each将被调用
  • 如果std::vector<T>::iteratormy::A *的typedef:编译器应该抱怨my::for_each不带3个参数

答案 2 :(得分:2)

您的选择是:

  • 定义一个在其实现中使用std :: pair的新类型,而不是使用typedef
  • 为输出功能使用其他名称
  • 在调用时明确限定所需的功能
  • (可能)专门化命名空间std中的函数(我不确定pair<int,double>是否算作UDT)

这一切都源于typedef的主要优点和缺点: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);

答案 3 :(得分:2)

您可以使用强类型设置:

#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。)

答案 4 :(得分:1)

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

struct myPair
{
  int first;
  double second;
};

答案 5 :(得分:1)

允许将模板函数的特化添加到namespace::std但是因为MyPair中使用的所有类型都不是用户定义的,所以我不是确定< / em>这样的专业化是合法的。

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

答案 6 :(得分:1)

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

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