Экспресс ограничения при использовании шаблонов C ++ - PullRequest
0 голосов
/ 29 октября 2018

У меня есть класс 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 Ответ

0 голосов
/ 29 октября 2018

Давайте начнем с того, почему то, что у вас сейчас есть, не работает. Функция шаблона (также конструкторы) идентифицируется по своей подписи. Эта подпись включает в себя то, что представляют собой аргументы шаблона (типы, не-типы, шаблоны), их порядок, а также аргументы функций и тип возвращаемого значения (конструктор не имеет возвращаемого типа, но это не имеет отношения к тому, что мы пытаемся выполнения). Итак, то, что вы пытались сделать, включало объявление одного и того же consturctor дважды! Прежде чем определение будет рассмотрено, у вас есть дубликат декларации, что, конечно, недопустимо.

Так что мы можем сделать? Мы можем добавить параметры для каждого конструктора. И если это их дифференцирует, они могут сосуществовать. Но сосуществования недостаточно, мы хотим, чтобы разрешение перегрузки относилось к ним по-разному. Мы хотим, чтобы первый шаблонный c'or был выбран для классов, производных от T. И второй для классов, производных от SomeClass. Можем ли мы сделать это? Да мы можем. Если мы сделаем параметр шаблона, который мы добавим, в зависимости от наших условий и неудачной замены, если условие не выполнено, эта перегрузка будет удалена из рассмотрения. Это SFINAE !

Таким образом, применяя все это на практике:

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 мы больше не получим двух конфликтующих объявлений конструктора.

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