Специализация класса шаблонов С++: почему необходимо повторить внедрение общих методов

В образце:

#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>.

Есть ли способ сделать такую ​​специальную специализацию без необходимости повторного внедрения методов?

Ответ 1

Нет, нет.

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

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

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

Ответ 2

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

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

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

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

Ответ 3

Проблема заключается в ошибочном предположении, что между 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 { /* ... */ }

Ответ 4

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

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

Ответ 5

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

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