在 std::vector 中存储模板摘要 类
Storing template abstract classes in std::vector
我正在尝试将一些 Java 代码翻译成 C++,但我在使用类型系统时遇到了一些问题。
我有一个 interface/pure 虚拟 class,它表示 table 中的一个列,我希望 table 是这些通用列的向量:
template<typename T>
class IColumn {
public:
virtual const std::string& name() = 0;
virtual size_t size() = 0;
virtual T at(size_t idx) = 0;
virtual std::vector<T> data() = 0;
virtual ~IColumn() = default;
};
class StringColumn : public IColumn<std::string> {
public:
StringColumn(std::string name, std::vector<std::string> data)
: name_(std::move(name)), data_(std::move(data)) {}
const std::string& name() override { return name_; }
size_t size() override { return data_.size(); }
const std::vector<std::string>& data() override { return data_; }
std::string at(size_t idx) override { return data_[idx]; }
private:
std::string name_;
std::vector<std::string> data_;
};
class IntColumn : public IColumn<int> {
public:
IntColumn(std::string name, std::vector<int> data)
: name_(std::move(name)), data_(std::move(data)) {}
const std::string& name() override { return name_; }
size_t size() override { return data_.size(); }
const std::vector<int>& data() override { return data_; }
int at(size_t idx) override { return data_[idx]; }
private:
std::string name_;
std::vector<int> data_;
};
但是,我在 Table
class:
中声明向量时遇到问题
class Table {
std::vector<std::unique_ptr<IColumn>> columns;
};
我知道 IColumn
是一个模板 class,因此它抱怨缺少模板参数,但是我不确定如何解决这个问题,因为虚函数 T at()
和 std::vector<T> data()
依赖于 T.
我该如何解决这个问题,或者有人可以为此提出替代设计 API?
编辑:按照建议将 std::variant<IntColumn, StringColumn>
与 std::visit
一起使用:
for (auto& col: columns_) {
std::cout << std::visit([](auto&& arg) { return arg.name(); }, col) << '\t';
}
std::cout << '\n';
for (int i = 0; i < this->rowCount(); i++) {
for (auto& col : columns_) {
auto v = std::visit([i](auto&& arg) { return arg.at(i); }, col);
std::cout << v;
}
}
第一个 std::visit
有效,但第二个无效。
谢谢。
也许我的方法适合您的需要。
这里我们添加 item_size()
方法和 return void*
而不是 T
class IColumn {
public:
virtual const std::string& name() = 0;
virtual size_t size() = 0;
virtual size_t item_size() = 0;
virtual void* at(size_t idx) = 0;
virtual void* data() = 0;
virtual ~IColumn() = default;
};
然后使用自定义类型实现列
template <class T>
class TColumn : public IColumn {
public:
TColumn(std::string name, std::vector<T> data) : name_(std::move(name)), data_(std::move(data)) {}
const std::string& name() override { /* */}
virtual size_t size() override { /* */ }
size_t item_size() override {
return sizeof(T);
}
void* at(size_t idx) override {
return &data_.at(idx);
}
void* data() override {
return data_.data();
}
private:
std::string name_;
std::vector<T> data_;
};
然后你可以做类似的事情
Table t;
std::vector<int> column = { 1, 2, 3 };
t.columns.emplace_back(std::make_unique<TColumn<int>>("name", column));
int item;
void* res = t.columns[0]->at(0);
std::memcpy(&item, res, sizeof(item));
std::cout << item << '\n';
IColumn
看起来很奇怪,你确实可以使用模板:
template <typename T>
class Column {
public:
Column(std::string name, std::vector<T> data) :
m_name(std::move(name)),
m_data(std::move(data))
{}
const std::string& name() const { return m_name; }
std::size_t size() const { return m_data.size(); }
const T& at(size_t idx) const { return m_data.at(idx); }
std::vector<T>& data() { return m_data; }
private:
std::string m_name;
std::vector<T> m_data;
};
和 std::variant
在 Table
struct Table
{
std::vector<std::variant<Column<std::string>, Column<int>>> columns;
};
用法可能类似于
Table table;
table.columns.push_back(Column<std::string>("string column", {"data1", "data2", "data3"}));
table.columns.push_back(Column<int>("int column", {4, 8, 15}));
const char* sep = "";
for (auto& col: table.columns) {
std::visit([&sep](const auto& arg) { std::cout << sep << arg.name(); }, col);
sep = "\t";
}
std::cout << "\n";
for (std::size_t i = 0; i < table.rowCount(); i++) {
const char* sep = "";
for (const auto& col : table.columns) {
std::visit([i, &sep](const auto& arg) { std::cout << sep << arg.at(i); }, col);
sep = "\t";
}
std::cout << std::endl;
}
我正在尝试将一些 Java 代码翻译成 C++,但我在使用类型系统时遇到了一些问题。
我有一个 interface/pure 虚拟 class,它表示 table 中的一个列,我希望 table 是这些通用列的向量:
template<typename T>
class IColumn {
public:
virtual const std::string& name() = 0;
virtual size_t size() = 0;
virtual T at(size_t idx) = 0;
virtual std::vector<T> data() = 0;
virtual ~IColumn() = default;
};
class StringColumn : public IColumn<std::string> {
public:
StringColumn(std::string name, std::vector<std::string> data)
: name_(std::move(name)), data_(std::move(data)) {}
const std::string& name() override { return name_; }
size_t size() override { return data_.size(); }
const std::vector<std::string>& data() override { return data_; }
std::string at(size_t idx) override { return data_[idx]; }
private:
std::string name_;
std::vector<std::string> data_;
};
class IntColumn : public IColumn<int> {
public:
IntColumn(std::string name, std::vector<int> data)
: name_(std::move(name)), data_(std::move(data)) {}
const std::string& name() override { return name_; }
size_t size() override { return data_.size(); }
const std::vector<int>& data() override { return data_; }
int at(size_t idx) override { return data_[idx]; }
private:
std::string name_;
std::vector<int> data_;
};
但是,我在 Table
class:
class Table {
std::vector<std::unique_ptr<IColumn>> columns;
};
我知道 IColumn
是一个模板 class,因此它抱怨缺少模板参数,但是我不确定如何解决这个问题,因为虚函数 T at()
和 std::vector<T> data()
依赖于 T.
我该如何解决这个问题,或者有人可以为此提出替代设计 API?
编辑:按照建议将 std::variant<IntColumn, StringColumn>
与 std::visit
一起使用:
for (auto& col: columns_) {
std::cout << std::visit([](auto&& arg) { return arg.name(); }, col) << '\t';
}
std::cout << '\n';
for (int i = 0; i < this->rowCount(); i++) {
for (auto& col : columns_) {
auto v = std::visit([i](auto&& arg) { return arg.at(i); }, col);
std::cout << v;
}
}
第一个 std::visit
有效,但第二个无效。
谢谢。
也许我的方法适合您的需要。
这里我们添加 item_size()
方法和 return void*
而不是 T
class IColumn {
public:
virtual const std::string& name() = 0;
virtual size_t size() = 0;
virtual size_t item_size() = 0;
virtual void* at(size_t idx) = 0;
virtual void* data() = 0;
virtual ~IColumn() = default;
};
然后使用自定义类型实现列
template <class T>
class TColumn : public IColumn {
public:
TColumn(std::string name, std::vector<T> data) : name_(std::move(name)), data_(std::move(data)) {}
const std::string& name() override { /* */}
virtual size_t size() override { /* */ }
size_t item_size() override {
return sizeof(T);
}
void* at(size_t idx) override {
return &data_.at(idx);
}
void* data() override {
return data_.data();
}
private:
std::string name_;
std::vector<T> data_;
};
然后你可以做类似的事情
Table t;
std::vector<int> column = { 1, 2, 3 };
t.columns.emplace_back(std::make_unique<TColumn<int>>("name", column));
int item;
void* res = t.columns[0]->at(0);
std::memcpy(&item, res, sizeof(item));
std::cout << item << '\n';
IColumn
看起来很奇怪,你确实可以使用模板:
template <typename T>
class Column {
public:
Column(std::string name, std::vector<T> data) :
m_name(std::move(name)),
m_data(std::move(data))
{}
const std::string& name() const { return m_name; }
std::size_t size() const { return m_data.size(); }
const T& at(size_t idx) const { return m_data.at(idx); }
std::vector<T>& data() { return m_data; }
private:
std::string m_name;
std::vector<T> m_data;
};
和 std::variant
在 Table
struct Table
{
std::vector<std::variant<Column<std::string>, Column<int>>> columns;
};
用法可能类似于
Table table;
table.columns.push_back(Column<std::string>("string column", {"data1", "data2", "data3"}));
table.columns.push_back(Column<int>("int column", {4, 8, 15}));
const char* sep = "";
for (auto& col: table.columns) {
std::visit([&sep](const auto& arg) { std::cout << sep << arg.name(); }, col);
sep = "\t";
}
std::cout << "\n";
for (std::size_t i = 0; i < table.rowCount(); i++) {
const char* sep = "";
for (const auto& col : table.columns) {
std::visit([i, &sep](const auto& arg) { std::cout << sep << arg.at(i); }, col);
sep = "\t";
}
std::cout << std::endl;
}