【问题标题】:C++ how to accept arbitrary length list of pairs at compile time?C ++如何在编译时接受任意长度的对列表?
【发布时间】:2022-01-15 13:20:59
【问题描述】:

我正在寻找构建一个编译时只读映射,并希望用std::arraystd::tuple 支持它,其中每个元素都是std::pair。为了便于使用,我想避免在构造时对每个条目进行注释,并且我希望它能够推断出地图中的元素数量,即:

constexpr MyMap<int, std::string_view> my_map{
  {1, "value1"},
  {2, "value2"},
};

我已经尝试了许多策略来做到这一点,但我似乎陷入了制作一个既能接受任意数量的元素又能告诉编译器所有大括号条目的 ctor 或函数pass (ex {1, "value1"}) 是一对,否则无法推断出类型。

例如:

template <typename Key, typename Mapped, typename... Args>
constexpr auto make_map(std::pair<Key, Mapped>&& first, Args&&... args) {
  if constexpr (sizeof...(Args) == 0) {
    return std::tuple{std::forward<decltype(first)>(first)};
  }
  return std::tuple_cat(
    std::tuple{std::forward<decltype(first)>(first)},
    make_map(std::forward<Args>(args)...)
  );
}

似乎我可以制作一个宏,它可以让我快速制作函数的版本,以将所有参数说成一个合理的数字(比如 10-15),但这感觉更丑更糟。

有没有办法做我想做的事,还是我需要求助于宏或让用户用std::pair注释每个条目?

【问题讨论】:

  • 我无法抓住重点!为什么不简单地使用像 struct X { int i; std::string_view s; }; X x[] { {1, "value1"}, {2, "value2"}, }; 这样的旧式数组。您想在编译时对其进行排序还是需要map?如图所示,只需存储值即可。

标签: c++ templates


【解决方案1】:

如果我理解正确,地图的大小是已知的并且是固定的?如果是这样,为什么不使用常规的 c 风格的数组构造函数呢?不幸的是,没有办法让编译器推断直接初始化列表的类型(例如:推断{1, "value"}std::pair&lt;int, std::string_view&gt;)所以,您必须指定类型才能进行推断。

#include <array>
#include <string_view>
#include <utility>

template <typename K, typename V, size_t N>
class MyMap {
 public:
  using value_type = std::pair<K, V>;

  constexpr explicit MyMap(value_type(&&init)[N])
      : data_(std::to_array(std::forward<value_type[N]>(init))) {}

  const std::array<value_type, N> data_;
};

template <typename K, typename V, size_t N>
constexpr MyMap<K, V, N> MakeMyMap(
    typename MyMap<K, V, N>::value_type(&&init)[N]) {
  return MyMap{std::forward<typename MyMap<K, V, N>::value_type[N]>(init)};
}

int main(int argc, char* argv[]) {
  constexpr std::string_view value_1 = "value1";
  constexpr std::string_view value_2 = "value2";

  constexpr auto my_map = MakeMyMap<int, std::string_view>({
      {1, value_1},
      {2, value_2},
  });

  static_assert(my_map.data_.at(0) == std::make_pair(1, value_1));
  static_assert(my_map.data_.at(1) == std::make_pair(2, value_2));

  return EXIT_SUCCESS;
}

注意:这是c++20 只是因为std::to_array (https://en.cppreference.com/w/cpp/container/array/to_array)。但是可以在c++17 中轻松实现这一点

#include <array>
#include <cstddef>
#include <type_traits>
#include <utility>

namespace internal {

template <bool Move = false, typename T, std::size_t... I>
constexpr std::array<std::remove_cv_t<T>, sizeof...(I)> to_array_impl(T (&a)[sizeof...(I)], std::index_sequence<I...>) {
  if constexpr (Move) {
    return {{std::move(a[I])...}};
  } else {
    return {{a[I]...}};
  }
}

}  // namespace internal

template <typename T, std::size_t N>
constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N]) noexcept(
    std::is_nothrow_constructible_v<T, T&>) {
  static_assert(!std::is_array_v<T>);
  static_assert(std::is_constructible_v<T, T&>);
  return internal::to_array_impl(a, std::make_index_sequence<N>{});
}

template <typename T, std::size_t N>
constexpr std::array<std::remove_cv_t<T>, N> to_array(T(&&a)[N]) noexcept(
    std::is_nothrow_move_constructible_v<T>) {
  static_assert(!std::is_array_v<T>);
  static_assert(std::is_move_constructible_v<T>);
  return internal::to_array_impl<true>(a, std::make_index_sequence<N>{});
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-15
    • 2020-06-27
    • 1970-01-01
    • 2013-10-06
    • 2015-07-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多