c++ - 强制使通用模板失败,但允许特化,加上 "method exists dispatching"

标签 c++ templates

有关模板/元编程技术改进的一些问题。

下面代码的动机是使用 C 风格的 API 方便地从 SQL 数据库驱动程序中提取正确类型的数据。结果集的行被一一返回为 char** 。我用下面的一些数组文字模拟了这一点。

我把这个包起来char**类中的行数据提供类型提取。还有一个模板化的 single_column()函数可以在调用者选择的容器中返回单一类型的列。

代码的工作原理如图所示。问题是:

  • 阻止 row::get<ValueType> 的非专业版本与 static_cast<void>看起来像黑客。有没有更好的办法?我认为答案不是简单地写 get_int() , get_long()等等,因为我们想要像 single_column() 这样的通用代码只选择指定的 getter。
  • 类型特征“方法存在调度”来检测 push_back() 的存在与 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);
}

最佳答案

blocking the non-specialised version of row::get with a static_cast seems like a hack. Is there a better way. I don't thing the answer is to simply write get_int(), get_long() etc, because we want generic code like single_column() to just select the specified getter.

您可以删除它:

template <typename ValueType>
ValueType get(unsigned idx) const = delete;

Demo

the type-trait "method exists dispatching" to detect presence of push_back() with if constexpr works fine. Is there a more elegant way using C++17? ie without c++20 concepts?

你的方法很好(创建和使用类型特征);
C++20 概念允许使用 requires 提供更好的语法。

关于c++ - 强制使通用模板失败,但允许特化,加上 "method exists dispatching",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69220893/

相关文章:

c++ - 当数据结构是模板参数时,如何判断操作是否会使迭代器无效?

c++ - 添加带有自定义标准错误的 fprintf() 语句后标准输出挂起

c++ - 在struct和main中分配内存的区别?

javascript - 我需要下划线模板做什么?

python - 如何使用 django 在生产服务器上修复模板不存在错误?

javascript - mustache 模式下划线模板中的循环

c++ - 如果模板类不尝试存储彼此的实例,它们可以相互依赖吗?

c++ - 在 DMA 之后反向更改为 int

c++ - 在类中存储单例指针

c++ - 错误 C2893 : Failed to specialize function template 'unknown-type std::less<void>::operator ()(_Ty1 &&,_Ty2 &&) const'