c++ - 强制失败通用模板但允许专业化,加上“方法存在调度”
问题描述
关于模板/元编程技术改进的一些问题。
下面代码的动机是方便地从具有 c 样式 API 的 sql db 驱动程序中提取正确类型的数据。结果集的行以 . 的形式逐一返回char**
。我用下面的一些数组文字来模拟它。
我将此char**
行数据包装在一个类中以提供类型提取。还有一个模板single_column()
函数,它可以在调用者选择的容器中返回一个单一类型的列。
代码如图所示工作。问题是:
row::get<ValueType>
用 a阻止非专业版本static_cast<void>
似乎是一种黑客行为。有没有更好的办法?我不认为答案是简单地编写get_int()
等get_long()
,因为我们想要通用代码,比如single_column()
只选择指定的 getter。- 类型特征“方法存在调度”以检测
push_back()
with的存在if constexpr
工作正常。有没有更优雅的方式使用 C++17?即没有 c++20 概念?
非常感谢
#include <cstddef>
#include <iostream>
#include <string>
#include <unordered_set>
#include <vector>
// is there a more direct way (without using c++20 concepts)
template <typename C, typename = void>
struct has_push_back : std::false_type {};
template <typename C>
struct has_push_back<
C, std::void_t<decltype(std::declval<C>().push_back(std::declval<typename C::value_type>()))>>
: std::true_type {};
class row {
public:
explicit row(const char** row) : row_(row) {}
template <typename ValueType>
ValueType get(unsigned idx) const {
// should always fail! user must call an existing specialisation!
// is there a "better" way?
return static_cast<void>(row_[idx]); // NOLINT ptr arith
}
template <>
[[nodiscard]] int get<int>(unsigned idx) const {
return std::stoi(row_[idx]); // NOLINT ptr arith
}
template <>
[[nodiscard]] long get<long>(unsigned idx) const {
return std::stol(row_[idx]); // NOLINT ptr arith
}
template <>
[[nodiscard]] std::string get<std::string>(unsigned idx) const {
return row_[idx]; // NOLINT ptr arith
}
template <>
[[nodiscard]] const char* get<const char*>(unsigned idx) const {
return row_[idx]; // NOLINT ptr arith
}
private:
const char** row_;
};
template <typename ContainerType>
ContainerType single_column(const std::vector<row>& rows, unsigned col = 0) {
ContainerType values;
using ValueType = typename ContainerType::value_type;
for (auto&& row: rows) {
if constexpr (has_push_back<ContainerType>::value) // is this "best" way?
values.push_back(row.get<ValueType>(col));
else
values.insert(row.get<ValueType>(col));
}
return values;
}
int main() {
// dealing with this as c-arrays because that is how it comes from db driver
// represents raw data coming back from db
// NOLINTNEXTLINE C-arrays
const char* data[][3] = {
{
"1-some text",
"122222222",
"1333333333333333",
},
{
"2-some text",
"222222222",
"2333333333333333",
},
{
"2-some text", // dupe value
"322222222",
"3333333333333333",
},
};
std::vector<row> rows; // simulationg rows being returned the db server
for (auto& r: data) rows.emplace_back(r);
// now retrieve column-wise as correct types into vectors
for (auto&& e: single_column<std::vector<std::string>>(rows, 0)) std::cout << e << ",";
std::cout << std::endl;
for (auto&& e: single_column<std::vector<int>>(rows, 1)) std::cout << e << ",";
std::cout << std::endl;
for (auto&& e: single_column<std::vector<long>>(rows, 2)) std::cout << e << ",";
std::cout << std::endl;
// now into unordered_sets
for (auto&& e: single_column<std::unordered_set<std::string>>(rows, 0)) std::cout << e << ",";
std::cout << std::endl;
for (auto&& e: single_column<std::unordered_set<int>>(rows, 1)) std::cout << e << ",";
std::cout << std::endl;
for (auto&& e: single_column<std::unordered_set<long>>(rows, 2)) std::cout << e << ",";
std::cout << std::endl;
// compile error - the static_cast<void> fails: works, but is there a "better" way
// double d = rows[0].get<double>(1);
}
解决方案
用 static_cast 阻止非专业版本的 row::get 似乎是一种 hack。有没有更好的办法。我不认为答案是简单地编写 get_int()、get_long() 等,因为我们希望像 single_column() 这样的通用代码只选择指定的 getter。
你可以delete
:
template <typename ValueType>
ValueType get(unsigned idx) const = delete;
类型特征“方法存在调度”以检测
push_back()
with的存在if constexpr
工作正常。有没有更优雅的方式使用 C++17?即没有 c++20 概念?
您的方式很好(创建和使用类型特征);
C++20 概念允许使用requires
.
推荐阅读
- google-data-studio - Data Studio:根据不同的时间范围计算同一变量的两个值之间的差异
- sql - 如何使用具有候选发布键的表编写查询?
- ios - 在 iOS 应用程序中查看主屏幕 (Swift)
- awk - 使用 awk 从不同时间读取的 2 个文件中的单行收集网络使用情况
- arrays - 在多个不同位置将数组写回电子表格
- sql - 如何将存储在表中的 SQL 提取为文本并使用它通过 teradata 存储过程将该查询的答案插入到另一个表中
- .net - 如何从 Azure 事件中心查看最近的数据?。网
- android - onActivityResult 在 API 4.4.4 中不起作用
- .htaccess - 将所有 URL 请求转发到另一个域(并保留原始请求的路径) - Htaccess 或 DNS?
- swift - 我收到有关缓存的“调用中的额外参数'forKey'”的错误