Обычные решения зависят от ADL. Первый - это написание шаблона функции, который выглядит так же, как в вашем вопросе:
namespace ns {
// vector must reside in this namespace
template<typename T>
std::ostream& operator<<(std::ostream& os, vector<T> const& v);
// define somewhere
}
Другой способ не требует написания шаблона, но навязчив, потому что он находится в определении класса:
namespace ns {
template<typename T>
class vector {
/* stuff */
public:
/* we're using the injected name for the class,
but vector<T> works just as well */
friend
std::ostream& operator<<(std::ostream& os, vector const&)
{
/* caveat: must be defined inline
there's no other way to write the definitions for all T */
}
};
}
В обоих случаях код клиента выглядит следующим образом:
std::ostream& os = /* a stream from somewhere */;
ns::vector<foo> v;
os << v; // adl picks up correct overload
Вероятно, вам следует использовать первую опцию, вторая обычно выбирается при реализации оператора для класса (т.е. вы пишете vector
в первую очередь). Обратите внимание, что из-за природы пространств имен вы можете снова открыть namespace ns
, чтобы поместить свой оператор здесь, даже если вы не пишете ns::vector
... за исключением случаев, когда речь идет о namespace std
, поскольку только в некоторых случаях допускается написание специализаций шаблонов (и, чтобы уточнить, шаблоны функций не могут быть частично специализированными, и мы не можем использовать здесь полную специализацию). Учитывая, что существует такая вещь, как std::vector
, возможно, вас заинтересует следующий последний вариант рва.
Что если вы хотите добавить перегрузку или написать шаблон функции, принимающий шаблон из namespace std
?
Это строго мое мнение, но я думаю, что есть только один разумный способ сделать это: поместить вашу функциональность в пространство имен по вашему выбору (которое, очевидно, не namespace std
, так как, опять же, его не разрешено помещать туда) и попросите код клиента.
namespace stream_extensions {
template<typename T>
std::ostream& operator<<(std::ostream& os, std::vector<T> const& v);
// define somewhere
}
Код клиента выглядит так:
std::ostream& os = /* stream from somewhere */;
std::vector<T> v;
// os << v; won't work, can't find relevant operator
using namespace stream_extensions;
os << v; // Okay, stream_extensions::operator<< is in scope
Если имя пространства имен выбрано правильно и ограничено только ограниченным числом вещей, я думаю, что такая директива using является очень разумным выбором. Рассмотрим using namespace literals::chrono;
, который позволяет написать, например, cond.wait(24_ms);
вместо cond.wait(std::chrono::milliseconds(24));
.
Большая оговорка этой техники в том, что функциональность должна быть совершенно новой . Это не способ эмулировать частичную специализацию для шаблона функции: если в namespace std
есть шаблон универсальной функции, то существует очень высокий риск того, что он будет предпочтен через ADL или вы получите перегрузку разрешение двусмысленности. Приведенный здесь пример имеет смысл, поскольку не существует неограниченной template<typename T> std::ostream& operator<<(std::ostream& os, T const& t);
(или аналогичной версии члена).