Проблема в том, что ваши специализации имеют один и тот же уровень (никто не более специализирован, чем другой), и Child<void>
соответствует обоим.
Если вы хотите, чтобы Child<void>
соответствовало кейсу Child<ReturnTy>
(в противном случае решение простое и элегантное: во второй специализации разделите список ParamTypes...
на обязательный тип Par0
и остальные ParamTypes...
). Я не вижу простого и элегантного решения.
На данный момент лучшее, что я могу себе представить, это добавить уровень косвенности (добавить класс Child_base
), добавив также параметр шаблона, чтобы явно указать желаемое решение.
Может быть, это можно сделать в более простой способ (извините, но в данный момент я могу попробовать с компилятором), но я представляю что-то следующее
template <typename RT, bool, typename ... PTs>
class Child_base : public Interface
{
// general case (no empy PTs... list and no void return type)
};
template <typename ... PTs>
class Child_base<void, true, PTs...> : public Interface
{
// case return type is void (also empy PTs... list)
};
template <typename RT>
class Child_base<RT, false> : public Interface
{
// case return type only, but not void, and empy PTs
};
template <typename RT, typename ... PTs>
class Child
: public Child_base<RT, std::is_same_v<void, RT>, PTs...>
{
};
Таким образом, Child<void>
наследуется от Child_base<void, true>
, что соответствует первой специализации Child_base
, но не соответствует второму.
Я предлагаю другой способ использования Child
: вместо определения его как класса, производного от Child_base
, вы можете попробовать определить его как using
псевдоним из Child_base
* 1 027 *
Возможно переименование Child_base
с более подходящим именем.