初始化C ++结构,例如在编译时绑定未知数组

时间:2019-02-15 16:33:23

标签: c++ templates c++17 variadic-templates

我可以编写它,并且效果很好:

struct Foo
{
  int i;
  std::string s;
};

const Foo foo[] = {
  { 42, "the answer to the ultimate questions" },
  { 23 /*initializing only the first member, 's' gets the default value*/ }
};

我想要做的是有一个包装数组的结构,以便可以向其中添加方法:

template<typename V1, typename V2, size_t Count>
struct Map
{
  std::array<std::pair<V1, V2>, Count> mappings;
  //or
  //std::pair<V1, V2> mappings[Count];

  V1 operator()(const V2&) const;
  V2 operator()(const V1&) const;
};

我想将其初始化为一个未知范围的数组,如下所示:

constexpr Map<int, std::string_view, /*???*/> = {
  { 42, "the answer to the ultimate question" },
  { 23, "some other stuff" },
  { /*...*/ }
};

但是随后出现一个问题,您需要指定一个我不想做的Count模板参数,我希望它像在数组情况下一样工作。

我认为返回此类对象的函数可以解决问题,就像这样:

template<typename V1, typename V2, typename... Args>
constexpr auto makeMap(Args... args)
{
  return Map<V1, V2, sizeof...(Args)>{ args... };
}

然后可以像这样使用它:

using Item = std::pair<int, std::string_view>;

constexpr auto map = makeMap<int, std::string_view>(
  Item{ 42, "the answer to the ultimate questions" },
  Item{ 23, "some other stuff" }
);

但是,如果省略Item类型,则模板实例化将无法推断出参数类型,这会禁止我本来想要的用法:

constexpr auto map = makeMap<int, std::string_view>(
  { 42, "the answer to the ultimate questions" },
  { 23, "some other stuff" }
);

目前,我认为这是不可能的,但无论如何我想问一下。

在研究此问题的同时,我发现a proposal恰好可以实现我想要的功能。

无论如何,我很想得到任何想法。

2 个答案:

答案 0 :(得分:5)

使用建议的to_array

template<typename V1, typename V2, size_t N>
constexpr auto makeMap(std::pair<V1, V2> const (&a)[N])
{
  return Map<V1, V2, N>{ to_array<std::pair<V1, V2>>(a) };
}

constexpr auto map = makeMap<int, std::string_view>({
  { 42, "the answer to the ultimate question" },
  { 23, "some other stuff" },
  { /*...*/ }
});

如果编译器支持库基础TS v2,则可以在名称空间to_array内的头文件<experimental/array>中找到std::experimental的实现。

答案 1 :(得分:1)

按照Jarod42的建议,我以递归的方式提出了一个递归的MakeMyMap结构,其中有一个static func(),该结构接收了一系列std::pair<T1, T2>自变量[观察:42是std::pair参数数目的默认上限]。

template <typename T1, typename T2, std::size_t Dim>
struct MyMap
 {
   std::array<std::pair<T1, T2>, Dim> map;
 };

template <typename T, std::size_t>
using getTheType = T;

template <typename, typename, typename = std::make_index_sequence<42u>>
struct MakeMyMap;

template <typename T1, typename T2, std::size_t ... Is>
struct MakeMyMap<T1, T2, std::index_sequence<Is...>>
   : public MakeMyMap<T1, T2, std::make_index_sequence<sizeof...(Is)-1u>>
 {
   using MakeMyMap<T1, T2, std::make_index_sequence<sizeof...(Is)-1u>>::func;

   static auto func (getTheType<std::pair<T1, T2>, Is> const & ... ps)
    { return MyMap<T1, T2, sizeof...(Is)>{ { { ps... } } }; }
 };

template <typename T1, typename T2>
struct MakeMyMap<T1, T2, std::index_sequence<>>
 {
   static auto func ()
    { return MyMap<T1, T2, 0u>{ }; }
 };

所以你可以写

   auto map = MakeMyMap<int, std::string>::func(
      { 42, "the answer to the ultimate questions" },
      { 23, "some other stuff" }
      );

下面是一个完整的编译示例(C ++ 14就足够了)

#include <array>
#include <string>
#include <utility>

template <typename T1, typename T2, std::size_t Dim>
struct MyMap
 {
   std::array<std::pair<T1, T2>, Dim> map;
 };

template <typename T, std::size_t>
using getTheType = T;

template <typename, typename, typename = std::make_index_sequence<42u>>
struct MakeMyMap;

template <typename T1, typename T2, std::size_t ... Is>
struct MakeMyMap<T1, T2, std::index_sequence<Is...>>
   : public MakeMyMap<T1, T2, std::make_index_sequence<sizeof...(Is)-1u>>
 {
   using MakeMyMap<T1, T2, std::make_index_sequence<sizeof...(Is)-1u>>::func;

   static auto func (getTheType<std::pair<T1, T2>, Is> const & ... ps)
    { return MyMap<T1, T2, sizeof...(Is)>{ { { ps... } } }; }
 };

template <typename T1, typename T2>
struct MakeMyMap<T1, T2, std::index_sequence<>>
 {
   static auto func ()
    { return MyMap<T1, T2, 0u>{ }; }
 };

int main ()
 {
   auto map = MakeMyMap<int, std::string>::func(
      { 42, "the answer to the ultimate questions" },
      { 23, "some other stuff" }
      );

   static_assert( std::is_same<decltype(map),
                               MyMap<int, std::string, 2u>>::value, "!" );
 }

使用C ++ 17,您可以使用std::string_view而不是std::string,可以定义constexpr func()函数,因此map可以是{{ 1}}

constexpr

您还可以验证

   constexpr auto map = MakeMyMap<int, std::string_view>::func(
      { 42, "the answer to the ultimate questions" },
      { 23, "some other stuff" }
      );

使用新的C ++ 17变体符号 static_assert( std::is_same<decltype(map), MyMap<int, std::string_view, 2u> const>::value, "!" ); ,您可以完全避免递归using,如下所示

MakeMyMap

其中template <typename, typename, typename = std::make_index_sequence<42u>> struct MakeMyMap; template <typename T1, typename T2, std::size_t ... Is> struct MakeMyMap<T1, T2, std::index_sequence<Is...>> : public MMM_helper<T1, T2, std::make_index_sequence<Is>>... { using MMM_helper<T1, T2, std::make_index_sequence<Is>>::func...; }; (“制作我的地图”助手)的定义如下

MMM_helper

以下是完整的C ++ 17(非递归)示例

template <typename, typename, typename>
struct MMM_helper;

template <typename T1, typename T2, std::size_t ... Is>
struct MMM_helper<T1, T2, std::index_sequence<Is...>>
 {
   static constexpr auto func (getTheType<std::pair<T1, T2>, Is> const & ... ps)
    { return MyMap<T1, T2, sizeof...(Is)>{ { { ps... } } }; }
 };