Специализация шаблона, где шаблонный тип также является шаблоном - PullRequest
5 голосов
/ 04 июня 2009

Я создал небольшую служебную функцию для преобразования строк, чтобы мне не приходилось создавать объекты ostringstream повсеместно

template<typename T>
inline string ToString(const T& x)
{
    std::ostringstream o;
    if (!(o << x))
        throw BadConversion(string("ToString(") + typeid(x).name() + ")");
    return o.str();
}

Я хочу предоставить некоторые специализации этого метода для случаев, когда по умолчанию нет перегруженного оператора << для stringstream (т.е. std :: pair, std :: set, мои собственные классы), и я столкнулся с трудностями с шаблонный. Я проиллюстрирую на примере std :: pair, если я хочу иметь возможность </p>

string str = ToString(make_pair(3, 4));

единственный способ сделать это - определить явную специализацию для int

template<>
inline string ToString(const pair<int,int>& x)
{
    std::ostringstream o;
    if (!(o << "[" << x.first << "," << x.second << "]"))
        throw BadConversion(string("ToString(pair<int,int>)"));
    return o.str();
}

Есть ли способ определить это для общего случая?

template<>
inline string ToString(const pair<T1,T2>& x)
{
    std::ostringstream o;
    if (!(o << "[" << x.first << "," << x.second << "]"))
        throw BadConversion(string("ToString(pair<T1,T2>)"));
    return o.str();
}   

Ответы [ 2 ]

16 голосов
/ 04 июня 2009

Не специализируйте шаблон, но перегрузите его. Компилятор выяснит, какой шаблон функции взять, упорядочив их в соответствии с их специализацией их типов параметров функции (это называется частичным упорядочением).

template<typename T1, typename T2>
inline string ToString(const std::pair<T1, T2>& x) {
    std::ostringstream o;
    if (!(o << "[" << x.first << "," << x.second << "]"))
        throw BadConversion(string("ToString(pair<T1,T2>)"));
    return o.str();
}

Как правило, частичное упорядочение приведет именно к тому, что вы ожидаете. Более подробно рассмотрим наличие этих двух функций

template<typename T> void f(T);
template<typename T, typename U> void f(pair<T, U>);

Теперь, чтобы увидеть, является ли один из них хотя бы столь же специализированным, как и другой, мы проверяем следующее для обоих шаблонов функций:

  1. выбрал какой-то уникальный тип для каждого параметра шаблона, подставив его в список параметров функции.
  2. Используя этот список параметров в качестве аргумента, сделайте вычет аргумента для другого шаблона (выполните виртуальный вызов с этими аргументами для этого другого шаблона). Если вычет успешен и преобразование не требуется (добавление const является таким).

Пример для вышесказанного:

  1. замена некоторого типа X1 на T дает нам некоторый тип, назовите его X1. вычет аргумента X1 против pair<T, U> не сработает. Так что первый не настолько специализирован, как второй шаблон.
  2. подстановка типов Y1 и Y2 в pair<T, U> приводит к pair<Y1, Y2>. Делание аргумента против T первого шаблона работает: T будет выведено как pair<Y1, Y2>. Так что второе, по крайней мере, столь же специализированное, как и первое.

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

Боюсь, этот обзор спешил, я сделал это только для параметров типа и пропустил некоторые детали. Загляните в 14.5.5.2 в C ++ Standard Spec, чтобы увидеть кровавые подробности. г

1 голос
/ 04 июня 2009

Как насчет использования признаков типа для сериализации различных типов в поток, как это:

template<typename T>
struct Traits {
  static inline bool ToStream(std::ostringstream& o, const T& x) {
    return o << x;
  }
};

template<typename T1, typename T2>
struct Traits<std::pair<T1, T2> > {
  static inline bool ToStream(std::ostringstream& o, const std::pair<T1, T2>& x) {
    return o << "[" << x.first << "," << x.second << "]";
  }
};

template<typename T>
inline std::string ToString(const T& x)
{
  std::ostringstream o;
  if (!Traits<T>::ToStream(o, x))
    return "Error";
  return o.str();
}

Примечание: "template <>" из части специализации не является обязательным, код без него компилируется нормально. Вы можете дополнительно добавить методы в признаки для сообщения об исключении.

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