Функция c ++ не может получить шаблон для дочернего класса базового класса с шаблонами - PullRequest
0 голосов
/ 14 мая 2018

Я немного новичок в шаблонах, и я не понимаю, как компилятор получает шаблоны для дочерних классов, когда я наследую от базового класса способом, которым я устанавливаю шаблоны.

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

template<typename T, typename S>
class individual {
    public:
        individual(S& fenotyp, T& genotyp) :
            fenotype(fenotyp), genotype(genotyp) {}
        ...
        S fenotype;

        T genotype;
        ...
};

Когда люди являются цепочками, у меня есть следующий дочерний класс:

class bitstring_individual : public individual<boost::dynamic_bitset<>,
    boost::dynamic_bitset<>> {
    public:
        using individual::individual;
        ...
};

Теперь мне больше не нужно работать с шаблонными скобками. Далее у меня есть функция, которая при численности населения std::vector<individual<T,S>> возвращает половину с наивысшей пригодностью. Это работает для любого типа индивидуума, поэтому мы можем сохранить общее определение:

template<typename T, typename S>
std::vector<individual<T,S>> select_best_half(std::vector<individual<T,S>> parents,
        std::vector<individual<T,S>> children) {
            ...
        }

Однако, если я вызываю эту функцию, я получаю error: no matching function for call to select_best_half(...), и компилятор говорит template argument deduction/substitution failed: и mismatched types ‘individual<T, S>’ and ‘bitstring_individual'.

В определении bitstring_individual мы видим, что:

bitstring_individual : individual<boost::dynamic_bitset<>,boost::dynamic_bitset<>>

так почему компилятор не понимает, что шаблоны должны быть boost::dynamic_bitset<>? Может кто-нибудь помочь мне понять, как компилятор справляется с этим наследованием и как я могу это исправить?

Ответы [ 2 ]

0 голосов
/ 14 мая 2018

Ваш вопрос на самом деле имеет дело с ковариацией и контравариантностью (определенных) типов в C ++.Даже если бы вы «жестко закодировали» параметры вашего шаблона, то есть имели:

using i_bs_bs = individual<bitset, bitset>;
using std::vector;

class bitstring_individual : public i_bs_bs { ... };

vector<bsi> select_best_half(vector<i_bs_bs> parents, vector<i_bs_bs> children) {
    ...
}

Вы все равно получили бы ошибку при передаче от vector<bitstring_individual> до select_best_half().Зачем?Потому что в C ++ std::vector<T> не является ковариантным конструктором типа .

Чтобы взять пример со страницы, связанной с Википедией, предположим, что ваши наследующие классы были Animal (базовый класс) и Cat (производный класс).В C ++ вы не можете добавить Animal к вектору Cats.Все элементы этого вектора должны быть кошками.Точно так же, как объясняет ответ @ MaxLanghof, вы не можете добавить bitstring_individual к вектору, элементы которого имеют базовый тип bitstring_individual.Любое специальное поведение, необходимое для обработки bitstring_individual, просто не будет применяться к элементам vector<i_bs_bs>.

0 голосов
/ 14 мая 2018

(using bitset = boost::dynamic_bitset)

Ваш bitstring_individual равен , а не так же, как individual<bitset, bitset>, и компилятор по праву не распознает их как таковые.Один наследует другой, да, но это не делает их взаимозаменяемыми везде - в частности, когда они используются в качестве аргументов шаблона.

Короче говоря: векторы (и другие контейнеры) различных (даже полиморфно связанных) типов не являются ковариантными,Также как вы не можете передать std::vector<int> функции, ожидающей std::vector<long>, вы не можете передать std::vector<bitstring_individual> там, где ожидается std::vector<individual<bitset, bitset>>.

Примечание: Да, это разные преобразования,но идея та же.

Представьте себе, что sizeof(individual<bitset, bitset>) = 32 и bitstring_individual добавляет некоторых членов, так что sizeof(bitstring_individual) = 48.Если компилятор выведет T = S = bitset, то он сгенерирует сигнатуру метода, содержащую std::vector<individual<bitset, bitset>>, то есть вектор, элементы которого имеют размер 32. Но когда вы пытаетесь вызвать его, вы передаете вектор, элементы которого имеют размер 48. Эти векторыне являются ковариантными, что неизбежно приведет к проблемам.

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

using bitstring_individual = individual<bitset, bitset>;

В противном случае, ваши векторы не могут хранить индивидуумы напрямую - вы должны будете использовать что-то вроде std::vector<std::shared_ptr<individual<T, S>>> (или unique_ptr или ref вместо shared_ptr) для всех векторов населения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...