частичная перегрузка шаблона - PullRequest
2 голосов
/ 18 марта 2012

Предположим, у меня есть функция с параметром, который перегружен многими различными типами.Например:

ostream& operator<<(ostream&, ...);

, поэтому, если у меня есть класс Foo

class Foo { ... }

, я могу определить перегрузку

ostream& operator<<(ostream&, const Foo&);

, и он отлично работает.

Теперь давайте предположим, что у меня есть другой класс:

template<class T>
class vector {...}

И теперь я хочу определить перегрузку функции, которая принимает любой vector<T>, где T может быть любым допустимым типом.Можно ли обойтись без определения его для всех возможных входных параметров?Какую подпись я должен использовать для такого определения?

template<class T>
ostream& operator<<(ostream&, const vector<T>& v);

?

Фон :

В этом случае я действительно хочу написатьперегрузка для std :: vector:

ostream& operator<<(ostream&, const std::vector<T>& x);

для записи чего-то вроде "{2, 4, 8}" или подобного, так как моя система журналирования построена поверх ostream и использует оператор << внутренне длятипы "stringify". </p>

Я думал, что задам вопрос в целом, но я должен добавить ограничение, что я не могу изменить класс (например, std :: vector).

Ответы [ 2 ]

4 голосов
/ 18 марта 2012

Обычные решения зависят от 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); (или аналогичной версии члена).

1 голос
/ 18 марта 2012

Подпись, которую вы дали, является правильной для использования:

template<class T>
ostream& operator<<(ostream&, const vector<T>& v);

Это не частичная специализация шаблона, а скорее базовая функция шаблона, которая будет соответствовать обычным правилам разрешения перегрузки функции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...