Подтвердить что ты не робот

ADL с typedefs из другого пространства имен

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

#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. Итак, вопросы:

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

Ответ 1

  • Вы можете создать свой собственный тип в пространстве имен N, возможно, наследуя от std:: pair. Вы можете добавить "using namespace N;" внутри основной. Первый, скорее всего, будет полезен.

  • Поскольку тип определен в другом пространстве имен и не может быть определен двумя.

Пример:

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 ctors with a using declaration
};
}

Если использование в качестве std:: пары не важно, вы можете удалить наследование и переименовать элементы. В любом случае вы можете, конечно, добавить дополнительные методы, но если вы сохраните наследование, вы можете использовать "методы переименования":

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

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

Ответ 3

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

  • Определите новый тип, который использует 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);

Ответ 4

Вы можете использовать сильный 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 по-прежнему необходим, чтобы избежать дополнительной запятой в макросе.)

Ответ 5

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

struct myPair
{
  int first;
  double second;
};

Ответ 6

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

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

Ответ 7

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

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