C ++: Наследование v. Содержание для шаблонного класса - PullRequest
0 голосов
/ 04 августа 2010

У меня есть следующая структура:

template <typename T>
struct Odp
{
    T m_t;
};

Я хочу специализировать его, чтобы я мог добавить оператор, чтобы тип хорошо работал с наборами STL. (Я не могу изменить Odp напрямую; это устаревший код.) Вот два способа сделать это:

struct Ftw : public Odp<int>
{
    bool operator==(const Ftw& rhs)
    {
        return m_t == rhs.m_t;
    } 
};

struct FtwContain
{
    Odp<int> odp;
    bool operator==(const FtwContain& rhs)
    {
        return odp.m_t == rhs.odp.m_t;
    }
};

Есть ли основания предпочесть второе первому? По-видимому, первый метод позволяет использовать более чистый код:

Ftw ftw;
ftw.m_t = 2;

FtwContain ftwContain;
ftwContain.odp.m_t = 2;

(Кроме того, есть вероятность, что я не совсем понимаю, что означает термин «специализация шаблона».)

Ответы [ 6 ]

7 голосов
/ 04 августа 2010

Я не верю, что нужно создавать новый тип - просто напишите бесплатную функцию:

template <typename T>
bool operator==( const Odp<T> & a, const Odp <T> & b ) {
    return a.m_t == b.m_t;
}
1 голос
/ 04 августа 2010

Вы действительно можете запутаться в терминологии. (Частичная) специализация шаблона обычно ссылается на конкретную реализацию шаблонного класса / структуры для выделенного типа. То есть у вас может быть общий шаблонный класс Hash, который предоставляет хеш-значения для типов, использующих метод getHash. Этот метод имеет общую реализацию, которая не заботится о типе, и, возможно, специальную реализацию для значений хеш-функции в строках:

// default implementation 
template<typename T> class Hash { int getHash(T val) { return val; } }
// string implementation
template<> class Hash<std::string> { int getHash(std::string val) { return val[0] || val[1]; } }

То, что вы делаете в примерах, - это не специализация шаблонов, а наследование (при первом подходе) и использование шаблона Odp в качестве клиента. В обоих случаях, если кто-либо использует шаблон Odp, как в Odp<int> odp, будет использоваться оригинальная реализация, которая может не соответствовать вашим ожиданиям. Если вы используете правильную специализацию шаблона, Odp<int> будет ссылаться на ваш специализированный код.

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

Как упоминает Нил, operator== вполне может быть бесплатной функцией.

Другой вариант: стандартная библиотека позволяет использовать пользовательские объекты предикатов.В этом случае:

#include <set>

template <typename T>
struct Odp
{
    T m_t;
};

struct CompareOdp
{
    template <class T>
    bool operator() (const Odp<T>& a, const Odp<T>& b) const
    {
        return a.m_t < b.m_t;
    }
};

int main()
{
    std::set<Odp<int>, CompareOdp > my_set;
    Odp<int> value = {10};
    my_set.find(value);
}

(Не уверен, что может быть лучше сделать весь предикат шаблоном. Создание только operator() шаблона облегчает его использование, так как оно оставляетбольше вещей, которые нужно выяснить компилятору. Не уверен, что в некоторых сценариях может произойти обратный выстрел.)


Также обратите внимание, что std :: set использует предикат для упорядочения (по умолчанию std::less<X>), не для тестов на равенство.

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

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

Возможные случаи для деривации

  1. Вы захотите получить, если вам нужно передать свой объект некоторымфункция, которая принимает тип базового класса
  2. Является ли производный класс типом первого типа.Если да, то да (например, плотоядное животное - это животное) 3.Если в базовом классе есть защищенные методы, которые вы хотите использовать в своем производном классе.Я не уверен, является ли упомянутая вами структура полным кодом или только соответствующим разделом.Если это не так, то это может быть одной из причин, по которой вы хотите вывести.

Возможные случаи для содержания

  1. Вы просто хотите использовать класс, и нет-отношения.TBH, можно также смоделировать is-a с содержащими объектами, где в типе контейнера действует как прокси-сервер для содержащегося типа (я думаю, что это шаблон проектирования, но я не уверен в названии шаблона).
  2. Вы заинтересованы в использовании только одного или двух методов, и не беспокоитесь об общем состоянии
  3. Этот объект никогда не передается ни в какой другой интерфейс, для которого требуется базовый класс (всегда можно передатьсодержащийся в нем объект, но выглядит грязным. Кроме того, в виртуальные функции добавляются разные вещи. Извините, я отвлекся).
0 голосов
/ 04 августа 2010

Я обычно предпочитаю композицию наследованию, но это действительно зависит от дизайна. Является ли Ftw типом Odp или Ftw содержит Odp.

Я бы не выбрал метод, основанный на более чистом коде (поскольку это не так уж много различий), я бы выбрал метод, основанный на концептуальной взаимосвязи между Odp и Ftw.

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

Почему бы не выводить Odp в MyOdp, вставить в него свой (общий) код и просто заставить Ftw наследовать от Odp (как в первом примере) или использовать typedef?Кстати, это не специализация, а конкретизация.Специализация шаблона - это когда вы (пере) определяете метод для определенного типа.

...