首页 > 解决方案 > 强制失败通用模板但允许专业化,加上“方法存在调度”

问题描述

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

下面代码的动机是方便地从具有 c 样式 API 的 sql db 驱动程序中提取正确类型的数据。结果集的行以 . 的形式逐一返回char**。我用下面的一些数组文字来模拟它。

我将此char**行数据包装在一个类中以提供类型提取。还有一个模板single_column()函数,它可以在调用者选择的容器中返回一个单一类型的列。

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

非常感谢

#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);
}

标签: c++templates

解决方案


用 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.


推荐阅读