Специализация класса шаблонов С++: почему необходимо повторить внедрение общих методов
В образце:
#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>
не имеет такого элемента. Очевидно, что в каждом случае нельзя использовать один и тот же испускаемый код.
Однако, если ваш конструктор по умолчанию ничего не делает (в любой из этих версий здесь), просто опустите его и разрешите компилятору выполнять работу.