Это связано с требованием отдельной компиляции и потому, что шаблоны являются полиморфизмом типа создания экземпляров.
Давайте немного приблизимся к конкретному для объяснения. Скажем, у меня есть следующие файлы:
class MyClass<T>
class MyClass<T>
MyClass<int>
. Отдельное средство компиляции. Я должен скомпилировать foo.cpp независимо от bar.cpp. Компилятор полностью выполняет всю сложную работу по анализу, оптимизации и генерации кода на каждом модуле компиляции; нам не нужно анализировать целую программу. Только компоновщик должен обрабатывать всю программу одновременно, и задача компоновщика значительно упрощается.
bar.cpp даже не нужно существовать при компиляции foo.cpp, но я все равно должен быть в состоянии связать foo.o Я уже имел вместе с bar.o Я только что выпустил, не перекомпилируя foo.cpp. foo.cpp может даже быть скомпилирован в динамическую библиотеку, распределенную где-то в другом месте без foo.cpp, и связан с кодом, который они пишут спустя годы после того, как я написал foo.cpp.
«Полиморфизм в стиле объектов» означает, что template MyClass<T>
не является общим классом, который может быть скомпилирован в код, который может работать для любого значения T
. Это добавит накладные расходы, такие как бокс, необходимо передать указатели на функции для распределителей и конструкторов и т. Д. Намерение шаблонов C ++ состоит в том, чтобы избежать необходимости писать почти идентичные class MyClass_int
, class MyClass_float
и т. Д., Но все же быть в состоянии закончить с компилируемым кодом, который в основном выглядит так, как если бы мы имели каждую версию отдельно. Таким образом, шаблон является буквально шаблоном; шаблон класса не класс, это рецепт создания нового класса для каждого T
, с которым мы сталкиваемся. Шаблон не может быть скомпилирован в код, только результат создания экземпляра шаблона может быть скомпилирован.
Итак, когда foo.cpp скомпилирован, компилятор не может видеть bar.cpp, чтобы знать, что MyClass<int>
необходимо. Он может видеть шаблон MyClass<T>
, но он не может испускать код для этого (это шаблон, а не класс). И когда компилируется bar.cpp, компилятор может видеть, что ему нужно создать MyClass<int>
, но он не может видеть шаблон MyClass<T>
(только его интерфейс в foo.h), поэтому он не может его создать.
Если foo.cpp сам использует MyClass<int>
, тогда код для него будет сгенерирован при компиляции foo.cpp, поэтому, когда bar.o связан с foo.o, они могут быть подключены и будут работать. Мы можем использовать этот факт, чтобы позволить конечный набор экземпляров шаблонов быть реализован в .cpp-файле, написав один шаблон. Но bar.cpp не может использовать шаблон в качестве шаблона и создавать его на всех типах, которые ему нравятся; он может использовать только ранее существовавшие версии шаблона, которые автор foo.cpp думал предоставить.
Вы можете подумать, что при компиляции шаблона компилятор должен «сгенерировать все версии», с теми, которые никогда не используются, отфильтровываются во время связывания. Помимо огромных накладных расходов и экстремальных трудностей, с которыми сталкивался такой подход, поскольку «модификаторы типа», такие как указатели и массивы, позволяют даже встроенным типам создавать бесконечное количество типов, что происходит, когда я расширяю свою программу добавив:
class BazPrivate
и использует MyClass<BazPrivate>
Невозможно, чтобы это могло работать, если мы либо
MyClass<T>
MyClass<T>
, чтобы компилятор мог генерировать MyClass<BazPrivate>
во время компиляции baz.cpp. Никто не любит (1), потому что системы компиляции целых программ принимают forever для компиляции и потому что это делает невозможным распространение компилированных библиотек без исходного кода. Итак, у нас есть (2).
Вы можете просто использовать аннотацию JPA @MapKey
(обратите внимание, что аннотация JPA отличается от аннотации Hibernate, Hibernate @MapKey
отображает столбец базы данных, содержащий ключ карты, в то время как аннотация JPA отображает свойство, которое будет использоваться в качестве ключа карты).
@javax.persistence.OneToMany(cascade = CascadeType.ALL)
@javax.persistence.MapKey(name = "name")
private Map<String, Person> nameToPerson = new HashMap<String, Person>();
@CollectionOfElements(fetch = FetchType.LAZY)
@JoinTable(name = "JOINTABLE_NAME",
joinColumns = @JoinColumn(name = "id"))
@MapKey(columns = @Column(name = "name"))
@Column(name = "age")
private Map<String, String> ages = new HashMap<String, String>();
Вам, вероятно, следует использовать UserType или UserCollectionType. Или вы можете использовать пользовательский tupleizer.
См. документацию по ядру hibernate для концепций и документацию по аннотациям hibernate для эквивалентного подхода к аннотациям.
Дайте мне знать, если это не то, о чем вы просите.