ADL с typedefs из другого пространства имен - PullRequest
15 голосов
/ 11 ноября 2010

У меня есть что-то вроде этого:

#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 N не связано с MyPair (к сожалению) Afaik нельзя добавлять в пространство имен std, поэтому, если я решу определить operator << в std, это будет как-то незаконно. Итак ... что делать в таких ситуациях? Я не хочу явно указывать operator << и не хочу писать using namespace N. Итак, вопросы:

  1. Как выполнить рефакторинг кода?
  2. Почему ADL не связывает пространства имен typedefs? Серьезные причины? Было бы хорошо, например в этом случае. Спасибо

Ответы [ 7 ]

4 голосов
/ 11 ноября 2010
  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; }
    
3 голосов
/ 11 ноября 2010

Я не могу вспомнить причину, по которой 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 является typedef для чего-то, что находится в пространстве имен std: std::for_each будет называться
  • Если std::vector<T>::iterator является typedef для my::A *: компилятор должен жаловаться, что my::for_each не принимает 3 аргумента
2 голосов
/ 14 января 2015

Вы можете использовать сильный 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;
}

(дополнительный typedef все еще необходим, чтобы избежать лишней запятой в макросе.)

2 голосов
/ 11 ноября 2010

Ваши варианты:

  • Определите новый тип, который использует std :: pair в своей реализации вместо typedef
  • Используйте другое имя для вашей функции вывода
  • Явно определяйте функцию, которую вы хотите, когда вы вызываете ее
  • (Может быть) Специализировать функцию в пространстве имен std (я не уверен, считается ли pair<int,double> UDT)

Все это проистекает из основной силы и слабости typedefs: имена 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);
1 голос
/ 12 июля 2014

Я решаю эту проблему, вытягивая соответствующие символы в пространство имен, из которого я хочу использовать их:

#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;
}
1 голос
/ 11 ноября 2010

Это позволяет добавить специализацию шаблонных функций к namespace::std, однако, поскольку ни один из типов, используемых в MyPair, не определен пользователем, я не уверен такая специализация законно.

namespace std {
     template<>
     ostream& operator<<(ostream& os, const MyPair& p) { }
}
1 голос
/ 11 ноября 2010

Если у вас есть определенный тип данных, который вы хотите вывести, вы всегда можете определить свой собственный класс вместо использования std::pair.

struct myPair
{
  int first;
  double second;
};
...