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

В образце:

#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 ответов

Решение

Нет, нет

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

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

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам