Функция шаблона, где один из аргументов нуждается в специализации, а другой - нет - PullRequest
0 голосов
/ 29 октября 2019

Я работаю в графе и хочу сделать некоторые поиски в ориентированном графе, в одном случае с ребрами, указывающими как обычно, а в другом случае с ребрами в обратном направлении. Края имеют время, чтобы пересечь их. Это время не обязательно является фиксированным значением или членом класса, и они могут иметь различные типы продолжительности (std::chrono::seconds, std::chrono::milliseconds, ...). Для каждого из двух случаев мне нужна функция, чтобы добавить время. Эта функция отличается в этих двух случаях, и для каждого она также должна быть шаблоном, потому что тип продолжительности может быть разным.

Моя идея состоит в том, чтобы сделать что-то вроде следующего:

class ForwardEdge;
class ReverseEdge;

template<typename DurationType>
std::chrono::time_point time_sum<ForwardEdge>(std::chrono::time_point tp, DurationType inc) {
  return tp + inc;
}
template<typename DurationType>
std::chrono::time_point time_sum<ReverseEdge>(std::chrono::time_point tp, DurationType inc) {
  return tp - inc;
}

Каков будет правильный способ сделать это?

Ответы [ 2 ]

1 голос
/ 29 октября 2019

Проблема в том, что вы можете частично специализировать шаблонную функцию.

Если вы принимаете решение на основе struct с (статической?) Шаблонной функцией, вы можете специализировать struct.

Я имею в виду ... что-то вроде

template <typename>
struct struct_sum;

template <>
struct struct_sum<ForwardEdge>
{
  template <typename DurationType>
  static std::chrono::time_point func(std::chrono::time_point tp,
                                      DurationType inc)
  { return tp + inc; }
};


template <>
struct struct_sum<ReverseEdge>
{
  template <typename DurationType>
  static std::chrono::time_point func(std::chrono::time_point tp,
                                      DurationType inc)
  { return tp - inc; }
};

, которое вы можете использовать таким образом

struct_sum<ForwardEdge>::func(tp, inc);
0 голосов
/ 29 октября 2019

Прежде чем вы сможете специализовать шаблон, вы сначала должны ввести его вообще:

template<typename Edge, typename DurationType>
std::chrono::time_point time_sum(std::chrono::time_point tp, DurationType inc);

template<typename DurationType>
std::chrono::time_point time_sum<ForwardEdge, DurationType>( /*...*/ )
//                                   ^^^            ^^^
// you still need to specify ALL template parameters!

Исключительно: это частичная специализация! Вам не разрешено частично специализировать функции, вам разрешено только делать это для типов .

Так что нам нужен вспомогательный класс:

template<typename Edge, typename DurationType>
struct TimeSum;

template<typename DurationType>
struct TimeSum<ForwardEdge, DurationType>
{
    std::chrono::time_point operator()(std::chrono::time_point tp, DurationType d)
    {
        return tp + d;
    }
};

template<typename DurationType>
struct TimeSum<ReverseEdge>
{
    std::chrono::time_point operator()(std::chrono::time_point tp, DurationType d)
    {
        return tp - d;
    }
};

Теперь мы можем использовать типы в совершенно не специализированной функции:

template<typename Edge, typename DurationType>
std::chrono::time_point time_sum(std::chrono::time_point tp, DurationType inc)
{
    return TimeSum<Edge, DurationType>()(tp, inc);
    //              ^^^
    // selects appropriate partial specialisation
}

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

template<typename Edge, typename DurationType>
{
    std::chrono::time_point operator()(std::chrono::time_point tp, DurationType d)
    {
        return tp + d;
    }
};

// and the specialisation for ReverseEdge from above

В этом сценарии у вас есть общий шаблон, который работает для любого типа, не охватываемого специализацией (включая ForwardEdge), и специализация, специфичная для ReverseEdge. Учитывая имя типа (TimeSum), этот вариант может быть более подходящим.

Поскольку параметр функции ребра отсутствует, вам необходимо указать аргумент шаблона вручную. Если у вас do все равно есть ребра, перегрузки могут быть лучшим выбором:

template<typename DurationType>
std::chrono::time_point time_sum
(
    ForwardEdge const&, // <--
    std::chrono::time_point tp, DurationType inc
);

template<typename DurationType>
std::chrono::time_point time_sum
(
    ReverseEdge const&, // <--
    std::chrono::time_point tp, DurationType inc
);

Другой альтернативой может быть наличие member функций в каждом классе (возможно, даже виртуальных,если они имеют общую базу).

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