Шаблоны и подклассы C ++? - PullRequest
2 голосов
/ 10 августа 2010

Итак, я изучаю C ++ и столкнулся с чем-то, что я знаю, как делать в Java, но не в C ++:).

У меня есть шаблон для объекта-контейнера, который определяется следующим образом:

template <class T>
class Container {
    vector<T> contained;

    public:

    void add(T givenObject) {
        this->contained.push_back(givenObject);
    }

    T get(string givenIdentifier) throw (exception) {
        for (int i = 0; i < this->contained.size(); i++) {
            if (this->contained[i].getIdentifier() == givenIdentifier) {
                return this->contained[i];
            }
        }
        throw new exception("An error has occured which has caused the object you requested to not be found. Please report this bug.");
    }

    bool empty() {
        return this->contained.empty();
    }

    bool identifierExists(string givenIdentifier) {
        for (int i = 0; i < this->contained.size(); i++) {
            if (this->contained[i].getIdentifier() == givenIdentifier) {
                return true;
            }
        }
        return false;
    }
};

Это на самом деле работает очень хорошо, с одной маленькой проблемой. Он сводится к двум строкам: первая - это определение шаблона, а вторая -

this->contained[i].getIdentifer()

В Java при объявлении Generic (шаблона) можно определить суперкласс / интерфейс, который должны расширяться всеми членами T, чтобы не создавать ошибку. Однако я не уверен в том, как сделать это в C ++, и меня беспокоит то, что связывание реализации здесь с методом getIdentifier, который не может быть определен, является плохим дизайном.

Теперь, это не так уж сложно, если это так, это всего лишь небольшой проект, который поможет мне выучить язык, но мне нравится стараться делать все правильно. Есть ли способ сделать то, что я думаю? Я знаю, что вы можете сделать это с примитивами, например:

template <int T>

допустимо, но когда я пытаюсь использовать пользовательский класс, я получаю ошибку компилятора. Есть предложения?

Ответы [ 4 ]

5 голосов
/ 10 августа 2010

Невозможно наложить искусственные ограничения на параметры типа шаблона.Если данный тип не поддерживает то, как вы его используете, вы получите ошибку компилятора.Функция под названием «концепции», которая, по сути, позволила бы это, собиралась добавить в следующий стандарт C ++, но она была отложена до следующего следующего стандарта из-за временных ограничений.Если T не имеет видимой функции getIdentifier(), экземпляр не будет скомпилирован.

Параметры шаблона должны быть получены во время компиляции.template<int T> допустимо, потому что первый параметр шаблона является целым числом;Вы можете создать его с любым постоянным целым числом.Если вы попытаетесь использовать его с неконстантной целочисленной переменной, он не скомпилируется.Экземпляр класса не является постоянной времени компиляции, поэтому его нельзя использовать.

3 голосов
/ 10 августа 2010

Вы получили пару других ответов, оба довольно хороших (особенно @ dauphic's, IMO).Я бы просто добавил, что код, который вы дали, действительно выглядит очень похоже на неэффективную имитацию std::map.В большинстве случаев std::map, вероятно, будет работать лучше.Если вы посмотрите его интерфейс, он также покажет вам способ отделить ваш контейнер от необходимости указывать getIdentifier() напрямую - вместо непосредственного использования чего-то вроде getIdentifier(), он использует функтор сравнения, который по умолчанию равен std::less<T>, который (в свою очередь) будет использовать T::operator< - но вы также можете указать совершенно другой функтор сравнения, если хотите.

Я должен также указать, что, в то время как другие указали, что вы получитеошибка компиляции, если вы используете getIdentifier (или что-то еще) и пытаетесь создать экземпляр класса, который его не предоставляет.Однако я чувствую себя обязанным предупредить вас, что сообщение об ошибке, которое вы получаете, может быть длинным, запутанным и довольно сложным для расшифровки.Это особенно вероятно, если есть какой-то другой тип, который имеет доступный член getIdentifier.В этом случае вы получите сообщение об ошибке вроде «Невозможно преобразовать из type A в type B», где type A - это любой тип, который вы использовали для создания экземпляра контейнера, а type B - любой (часто совершенно не связанный) тип, в котором есть getIdentifier член.Происходит то, что компилятор видит, что вы использовали getIdentifier, и видит, что type B имеет это, поэтому он пытается преобразовать ваш type A объект в type B объект и обнаруживает, что он не может,так что это , о чем говорится в сообщении об ошибке.

PS Да, я знаю, что это действительно больше комментарий, чем ответ.Я прошу прощения за это, но это не очень подходит для комментария.

1 голос
/ 10 августа 2010

Вам не нужно ничего делать.Если contained[i] не имеет функции getIdentifer(), вы получите ошибку во время компиляции (так же, как если бы вы использовали интерфейс, противоположный Java, и так же, как вы бы использовали шаблоны).* Чтобы уточнить: Если бы вы должны были написать,

 int x = 10;
 long id = x.getIdentifer();

Это будет считаться "плохим дизайном".Это было бы просто ошибкой, которую поймает компилятор.Это именно то, что произойдет в вашем примере.

0 голосов
/ 10 августа 2010

C ++ проводит довольно четкую грань между полиморфизмом во время компиляции (т.е. шаблонами) и полиморфизмом во время выполнения (т.е. наследованием). То, что вы хотите сделать, не поддерживается языком.

Одна из типичных практик, которая делает то, что вы делаете, - это, в дополнение к T, тип, который может получить идентификатор данного T. Это разделяет два поведения на два типа, и вы можете указать (на английском языке) интерфейс, который должен быть реализован.

template <class T, class StringIdForT>
class Container 
{
  ...
   bool identifierExists(string givenIdentifier) 
   {
        StringIdForT idGetter;
        for (int i = 0; i < this->contained.size(); i++) 
        {
            if (idGetter.getIdentifier(this->contained[i]) == givenIdentifier) 
            {
                return true;
            }
        }
        return false;
   }  
};

У вас похожая проблема, StringIdForT должен по-прежнему определять указанный метод

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