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