Специализация класса шаблонов C ++: почему общие методы должны быть повторно реализованы - PullRequest
14 голосов
/ 09 января 2012

В примере:

#include <iostream>

using namespace std;

class B
{
public:
    virtual void pvf() = 0;
};

template <class T>
class D : public B
{
public:
    D(){}

    virtual void pvf() {}

private:
    string data;
};

template <>
class D<bool> : public B
{
public:
    D();

    virtual void pvf(){ cout << "bool type" << endl; }
};

int main()
{
    D<int> d1;
    D<bool> d2;
}

Я получаю следующую ошибку:

test.cpp:(.text+0x1c): undefined reference to `D<bool>::D()'

Обратите внимание, что причина, по которой я не просто специализирую D () сама по себея хочу устранить необходимость в строке D<T>::data в случае D<bool>.

Почему мне нужно повторно внедрить D() в D<bool>?Похоже, у меня должен быть способ сказать компилятору использовать версию из D<T>.

Можно ли как-то просто выполнить такую ​​специализацию без необходимости повторной реализации методов?

Ответы [ 5 ]

16 голосов
/ 09 января 2012

Каждая специализация шаблона класса дает свой класс - они не делят членов друг с другом. Поскольку вы явно специализировали весь класс, вы не получаете ни одного члена из шаблона и должны реализовать их все.

Вы можете явно специализировать отдельных членов, а не весь класс:

template <> void D<bool>::pvf(){ cout << "bool type" << endl; }

Тогда D<bool> будет по-прежнему содержать все члены шаблона класса, которые вы не специализировали явно, включая конструктор по умолчанию.

11 голосов
/ 09 января 2012

Нет, нет.

Специализация ведет себя совсем не так, как наследование.Он не связан с общей версией шаблона.

Когда вы используете / создаете шаблон, компилятор создает новое имя типа, а затем ищет способ определения этого типа.Когда он находит специализацию, он принимает это как определение для нового типа.Когда этого не происходит, он берет общий шаблон и создает его экземпляр.

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

5 голосов
/ 09 января 2012

Проблема в том, что вы ошибочно полагаете, что есть что-то общее между D<A> и D<B>. Экземпляры шаблона: типы , а два разных экземпляра - это два разных типа, конец истории. Только так бывает, что экземпляры одного и того же шаблона имеют формально похожий код, но со специализацией вы можете определить любой тип, который вам нравится. Короче говоря, каждый тип, который вы определяете явно, является полностью независимым, и между специализированными экземплярами шаблона нет общего, даже если они имеют одно и то же имя.

Например:

template <typename T> struct Foo
{
    T & r;
    const T t;
    void gobble(const T &);
    Foo(T *);
};

template <> struct Foo<int>
{
    std::vector<char> data;
    int gobble() const;
    Foo(bool, int, Foo<char> &);
};

Типы Foo<char> и Foo<int> не имеют ничего общего друг с другом, и нет никаких причин, по которым какая-либо часть одного должна иметь какое-либо применение внутри другого.

Если вы хотите выделить общие черты, используйте частное наследование:

template <typename> struct D : private DImpl { /* ... */ }
1 голос
/ 09 января 2012

Вам необходимо переопределить его, потому что D<T> и D<bool> - совершенно не связанные классы (они просто "разделяют имя"). Вот так работают шаблоны.

Если вы хотите, чтобы классы совместно использовали конструкционный код, просто поместите этот код внутрь B::B (т.е. то же самое, что вы делаете каждый раз, когда вы хотите повторно использовать код в разных ветвях одной и той же иерархии: переместите код вверх и разрешите наследование справиться с остальным).

0 голосов
/ 09 января 2012

Учтите, что D<T>::D() будет отвечать за построение по умолчанию string data, и что D<bool> не имеет такого члена.Ясно, что нет способа использовать один и тот же код в каждом случае.

Однако, если ваш конструктор по умолчанию ничего не делает (в или версии здесь), просто опустите его и разрешитекомпилятор для выполнения работы.

...