Экспресс ограничения при использовании шаблонов C++
У меня есть класс Wrapper. Любые T или объекты, полученные из T, должны быть конвертируемыми в этот Обертку.
Я также хотел бы, чтобы любой объект из Someclass или объекты, производные от SomeClass, были конвертируемыми в Wrapper. Реализация в обоих случаях должна быть отдельной. Как мне этого добиться?
Вот то поведение, которое я хотел бы:
class SomeClass;
template <typename T>
class Wrapper
{
public:
Wrapper(const T & val)
{
}
template <typename E>
Wrapper(const E & val)
{
static_assert(std::is_base_of<T, E>::value,"Wrapped object needs to be of type T or a type derived from T");
// Some implementation
}
// Is it possible to combine the above 2 constructors into a single
// one? That would help too...
// Can't use SomeClass directly as type because in case of derived
// type of SomeClass, I want the exact type for downstream processing
// into another template call
template <typename E> // ??
Wrapper(const E & val)
{
static_assert(std::is_base_of<SomeClass, E>::value,"Wrapped object needs to be of type SomeClass or a type derived from SomeClass");
// another implementation
}
};
Не уверен, что смог правильно выразить свою проблему. Любая помощь очень ценится...
1 ответ
Давайте начнем с того, почему то, что у вас сейчас есть, не работает. Функция шаблона (также конструкторы) идентифицируется по своей подписи. Эта сигнатура включает в себя то, что представляют собой аргументы шаблона (типы, не-типы, шаблоны), их порядок, а также аргументы функций и тип возвращаемого значения (конструктор не имеет возвращаемого типа, но это не имеет отношения к тому, что мы пытаемся выполнения). Итак, то, что вы пытались сделать, включало объявление одного и того же consturctor дважды! Прежде чем определение будет рассмотрено, у вас есть дубликат декларации, что, конечно, не допускается.
Так что мы можем сделать? Мы можем добавить параметры к каждому конструктору. И если это их дифференцирует, они могут сосуществовать. Но сосуществования недостаточно, мы хотим, чтобы разрешение перегрузки рассматривало их по-разному. Мы хотим, чтобы первый шаблонный c'or был выбран для классов, производных от T. И второй для классов, производных от SomeClass
, Можем ли мы сделать это? Да мы можем. Если мы сделаем параметр шаблона, который мы добавим, в зависимости от наших условий и неудачной замены, если условие не выполнено, эта перегрузка будет удалена из рассмотрения. Это СФИНАЕ!
Таким образом, применяя все это на практике:
template <typename E, std::enable_if_t<!std::is_same<SomeClass,T>::value &&
std::is_convertible<E*, T*>::value>* = nullptr>
Wrapper(const E & val)
{
}
template <typename E, std::enable_if_t<std::is_convertible<E*, SomeClass*>::value>* = nullptr>
Wrapper(const E & val)
{
}
Так что же делать выше? Он добавляет еще один параметр шаблона с аргументом по умолчанию. Это делается условно, и если во время подстановки условие не выполняется, подпись "плохого" c'or плохо сформирована. И это не будет учитываться при разрешении перегрузки.
Я также позволил себе адаптировать ваше состояние. Вы, вероятно, хотите только классы, которые публично происходят от T
а также SomeClass
быть принятым. Это выражается лучше std::is_convertible
, std::is_base_of
позволит частное и неоднозначное множественное наследование тоже. Я также убедился, что в случае T
является SomeClass
мы не получаем два конфликтующих объявления конструктора снова.