Переопределение pure virtual метода базового класс для определённого типа в классе наследнике С++
Имеется базовый класс с чисто виртуальным методом
template <class Key, class Value>
class AbstractContainer {
public:
virtual ~AbstractContainer() = 0;
virtual const std::size_t Upload(std::ifstream &is) = 0;
};
Он наследуется для следующего класса.
template <class Key, class Value>
class HashTable : virtual public AbstractContainer<Key, Value> {
public:
HashTable();
~HashTable();
virtual const std::size_t Upload(std::ifstream &is) override final;
};
А метод переопределяется следующим образом.
template <class Key, class Value>
HashTable<Key, Value>::~HashTable() {}
template <>
const std::size_t HashTable<std::string, Storage>::Upload(std::ifstream &is) {
std::size_t loaded{0};
Storage storage;
std::istringstream stream;
static const std::regex regex(R"(^\w{1,15}\s+\S+\s+\S+\s+\d{4}\s+\S+\s+\d+(\s+)?$)");
for(std::string line, key; std::getline(is, line); stream.clear()) {
if (stream.str(line), std::regex_match(line, regex)) {
stream >> key;
stream >> storage;
loaded += Set(key, storage);
}
}
return loaded;
}
Как переопределить метод на следующие типы, оставив в базовом класе метод чисто виртуальным и при этом избавиться от ошибки?
[build] duplicate symbol 'virtual thunk to HashTable<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, Storage>::Upload(std::__1::basic_ifstream<char, std::__1::char_traits<char> >&)' in:
[build] CMakeFiles/Transaction.dir/interface/interface.cc.o
[build] CMakeFiles/Transaction.dir/main.cc.o
[build] duplicate symbol 'HashTable<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, Storage>::Upload(std::__1::basic_ifstream<char, std::__1::char_traits<char> >&)' in:
[build] CMakeFiles/Transaction.dir/interface/interface.cc.o
[build] CMakeFiles/Transaction.dir/main.cc.o
hash_table.h
#ifndef TRANSACTION_SRC_TABLE_HASH_TABLE_H_
#define TRANSACTION_SRC_TABLE_HASH_TABLE_H_
#include <algorithm>
#include <chrono>
#include <functional>
#include <iostream>
#include <limits>
#include <list>
#include <memory>
#include <tuple>
#include <regex>
#include "container.h"
#include "storage.h"
template <class Key, class Value>
class HashTable : virtual public AbstractContainer<Key, Value> {
public:
HashTable();
HashTable(HashTable &other);
HashTable(HashTable &&other);
~HashTable();
virtual const bool Set(const Key &key, const Value &value) override final;
[[nodiscard]] virtual std::optional<Value> Get(const Key &key) override final;
[[nodiscard]] virtual const bool Exists(const Key &key) override final;
virtual const bool Del(const Key &key) override final;
virtual const bool Update(const Key &key, const Value &value) override final;
[[nodiscard]] virtual std::vector<Key> Keys() override final;
virtual const bool Rename(const Key &key, const Key &rename) override final;
[[nodiscard]] virtual const std::optional<std::size_t> TTL(const Key &key) override final;
[[nodiscard]] virtual std::vector<Key> Find(const Value &value) override final;
virtual void Showall() override final;
// virtual const std::size_t Upload(std::ifstream &is) override final;
// virtual const std::size_t Export(std::ofstream &os) override final;
HashTable &operator=(HashTable &other);
HashTable &operator=(HashTable &&other);
private:
std::size_t m_mpn_cur_idx;
static const std::vector<std::size_t> m_mpn;
std::vector<std::list<std::shared_ptr<std::pair<Key, Value> > > > m_data;
void ResizeHashTable();
[[nodiscard]] std::tuple<typename std::list<std::shared_ptr<std::pair<Key, Value> > >::iterator, std::size_t> GetListIteratorByKey(const Key &key);
};
#include "hash_table.tpp"
#endif // TRANSACTION_SRC_TABLE_HASH_TABLE_H_
hash_table.tpp
template <class Key, class Value>
const std::vector<std::size_t> HashTable<Key, Value>::m_mpn{
3,
7,
31,
127,
8'191,
131'071,
524'287,
2'147'483'647,
2'305'843'009'213'693'951,
std::numeric_limits<std::size_t>::max()};
template <class Key, class Value>
HashTable<Key, Value>::HashTable() : m_mpn_cur_idx{0} {
m_data.resize(m_mpn[m_mpn_cur_idx]);
}
template <class Key, class Value>
HashTable<Key, Value>::HashTable(HashTable &other) {
*this = other;
}
template <class Key, class Value>
HashTable<Key, Value>::HashTable(HashTable &&other) {
*this = other;
}
template <class Key, class Value>
HashTable<Key, Value>::~HashTable() {}
template <class Key, class Value>
[[nodiscard]] std::tuple<typename std::list<std::shared_ptr<std::pair<Key, Value> > >::iterator, std::size_t> HashTable<Key, Value>::GetListIteratorByKey(const Key &key) {
std::size_t idx = std::hash<Key>{}(key) % m_mpn[m_mpn_cur_idx];
auto item = std::find_if(m_data[idx].begin(), m_data[idx].end(), [&](const std::shared_ptr<std::pair<Key, Value> > &pair) {
return pair->first == key;
});
return std::make_tuple(item, idx);
}
template <class Key, class Value>
const bool HashTable<Key, Value>::Set(const Key &key, const Value &value) {
auto [item, idx] = GetListIteratorByKey(key);
bool seted{m_data[idx].end() == item};
if (seted) {
m_data[idx].push_back(std::make_shared<std::pair<Key, Value> >(key, value));
if (++this->m_amount_elements == m_mpn[m_mpn_cur_idx]) {
ResizeHashTable();
}
}
return seted;
}
template <class Key, class Value>
void HashTable<Key, Value>::ResizeHashTable() {
decltype(m_data) temp(m_mpn[++m_mpn_cur_idx]);
for (auto &list: m_data) {
for (auto &pair: list) {
std::size_t idx = std::hash<Key>{}(pair->first) % m_mpn[m_mpn_cur_idx];
temp[idx].push_back(pair);
}
}
m_data = std::move(temp);
}
template <class Key, class Value>
[[nodiscard]] std::optional<Value> HashTable<Key, Value>::Get(const Key &key) {
auto [item, idx] = GetListIteratorByKey(key);
std::optional<Value> result;
if (m_data[idx].end() != item) {
result = (*item)->second;
}
return result;
}
template <class Key, class Value>
[[nodiscard]] const bool HashTable<Key, Value>::Exists(const Key &key) {
auto [item, idx] = GetListIteratorByKey(key);
return m_data[idx].end() != item;
}
template <class Key, class Value>
const bool HashTable<Key, Value>::Del(const Key &key) {
auto [item, idx] = GetListIteratorByKey(key);
bool deleted{m_data[idx].end() != item};
if (--this->m_amount_elements, deleted) {
m_data[idx].erase(item);
}
return deleted;
}
template <class Key, class Value>
const bool HashTable<Key, Value>::Update(const Key &key, const Value &value) {
auto [item, idx] = GetListIteratorByKey(key);
bool updated{m_data[idx].end() != item};
if (updated) {
(*item)->second = value;
}
return updated;
}
template <class Key, class Value>
[[nodiscard]] std::vector<Key> HashTable<Key, Value>::Keys() {
std::vector<Key> result;
for (auto &list: m_data) {
for (auto &item: list) {
result.push_back(item->first);
}
}
return result;
}
template <class Key, class Value>
const bool HashTable<Key, Value>::Rename(const Key &key, const Key &rename) {
auto [item, idx] = GetListIteratorByKey(key);
bool renamed{m_data[idx].end() != item};
if (renamed) {
(*item)->first = rename;
}
return renamed;
}
template <class Key, class Value>
[[nodiscard]] const std::optional<std::size_t> HashTable<Key, Value>::TTL(const Key &key) {
auto [item, idx] = GetListIteratorByKey(key);
std::optional<std::size_t> seconds;
if (m_data[idx].end() != item && (*item)->second.GetRemovalTime().has_value()) {
auto time_1 = std::chrono::steady_clock::now();
auto time_2 = (*item)->second.GetRemovalTime().value();
seconds = std::chrono::duration_cast<std::chrono::seconds>(time_2 - time_1).count();
}
return seconds;
}
template <class Key, class Value>
std::vector<Key> HashTable<Key, Value>::Find(const Value &value) {
std::vector<Key> result;
for (auto &list: m_data) {
for (auto &item: list) {
if (item->second == value) {
result.push_back(item->first);
}
}
}
return result;
}
template <class Key, class Value>
void HashTable<Key, Value>::Showall() {
std::size_t iteration{1};
for (auto &list: m_data) {
for (auto &item: list) {
std::cout << "> " << iteration++ << " ";
std::cout << item->second << "\n";
}
}
}
// template <>
// const std::size_t HashTable<std::string, Storage>::Upload(std::ifstream &is) {
// std::size_t loaded{0};
// Storage storage;
// std::istringstream stream;
// static const std::regex regex(R"(^\w{1,15}\s+\S+\s+\S+\s+\d{4}\s+\S+\s+\d+(\s+)?$)");
// for(std::string line, key; std::getline(is, line); stream.clear()) {
// if (stream.str(line), std::regex_match(line, regex)) {
// stream >> key;
// stream >> storage;
// loaded += Set(key, storage);
// }
// }
// return loaded;
// }
// template <>
// const std::size_t HashTable<std::string, Storage>::Export(std::ofstream &os) {
// std::size_t unloaded{0};
// for (auto &list: m_data) {
// for (auto &item: list) {
// os << item->first << " " << item->second;
// if (++unloaded != this->m_amount_elements) {
// os << '\n';
// }
// }
// }
// return unloaded;
// }
template <class Key, class Value>
HashTable<Key, Value> &HashTable<Key, Value>::operator=(HashTable &other) {
if (this != &other) {
m_data.clear();
m_data.resize(m_mpn[m_mpn_cur_idx = {}]);
this->m_amount_elements = {};
for (auto &list: other.m_data) {
for (auto &item: list) {
Set(item->first, item->second);
}
}
}
return *this;
}
template <class Key, class Value>
HashTable<Key, Value> &HashTable<Key, Value>::operator=(HashTable &&other) {
if (this != &other) {
this->m_amount_elements = {};
m_mpn_cur_idx = other.m_mpn_cur_idx;
m_data = std::move(other.m_data);
other.m_mpn_cur_idx = {};
other.m_amount_elements = {};
}
return *this;
}
container.tpp
template <class Key, class Value>
AbstractContainer<Key, Value>::~AbstractContainer() {}
template <class Key, class Value>
[[nodiscard]] const std::size_t AbstractContainer<Key, Value>::GetAmountElements() const {
return m_amount_elements;
}
Источник: Stack Overflow на русском