TAGS :Viewed: 8 - Published at: a few seconds ago

[ ADL with typedefs from another namespace ]

I have something like this:

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

This naturally doesn't work, because ADL won't find operator<< because namespace N is not associated with MyPair (unfortunately). Afaik one may not add to namespace std, so if I chose to define operator << in std that would be kinda illegal. So... what to do in such situations? I don't want to explicitly qualify operator <<, nor do I wish to write using namespace N. So, questions are:

  1. How to refactor the code?
  2. Why wouldn't ADL associate namespaces of typedefs? Serious reasons? It would be nice, e.g. in this case. Thanks

Answer 1


  1. You could create your own type in namespace N, possibly inheriting from std::pair. You could add "using namespace N;" inside main. The former is more likely to be useful.

  2. Because the type is defined in another namespace and cannot be defined in two.

Example:

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

If being used as a std::pair isn't important, you can drop the inheritance and rename the members. In either case you can, of course, add additional methods, but if you keep the inheritance you can use "renaming methods":

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

Answer 2


I can't think of a reason why typedef names should not participate in ADL. Furthermore, it makes the following code implementation defined :

#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(), [...]);
} 
  • If std::vector<T>::iterator is a typedef for something which sits in the std namespace : std::for_each will be called
  • If std::vector<T>::iterator is a typedef for my::A * : the compiler should complain that my::for_each doesn't take 3 arguments

Answer 3


Your options are to:

  • Define a new type which uses std::pair in its implementation, instead of using typedef
  • Use a different name for your output function
  • Explicitly qualify the function you want when you call it
  • (Maybe) Specialize the function in namespace std (I'm not sure if pair<int,double> counts as a UDT)

This all stems from the main strength and weakness of typedefs: typedef names are just synonyms. It doesn't matter what namespace you put it in, the typedef name refers to the associated type, in whatever namespace that type is defined in. This is distinct from a typedef being a new type that is convertible to/from the associated type. Imagine this scenario:

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

This is invalid, because int and id_t aren't distinct types. This extends to 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

And here's a question for you: Do you believe that the following should fail to compile? If not, how should the name lookup be resolved:

B::id_t id;
g(id);

Answer 4


You can use a strong typedef:

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

(The extra typedef is still necessary to avoid the extra comma in the macro.)

Answer 5


If you have a specific data type which you want to output, you can always define your own class rather than use std::pair.

struct myPair
{
  int first;
  double second;
};

Answer 6


It is allowed to add specialization of template functions to namespace::std however since none of the types used in MyPair is user defined I'm not sure such a specialization is legal.

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

Answer 7


I solve this problem by pulling the relevant symbol(s) into the namespace I want to use them from:

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