【问题标题】:Initialize a C++ struct like an array of unknown bound at compile time在编译时初始化 C++ 结构,如未知边界数组
【发布时间】:2019-02-15 16:33:23
【问题描述】:

我可以写这个,而且效果很好:

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 正是我想要的。

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

【问题讨论】:

  • 您添加一个将 std::array<:pair>> 作为输入的 ctor,这样您就可以使用初始化列表初始化数组
  • @Claudiu Guiman, std::array&lt;Whatever, Count&gt; 将无法从初始化列表中推导出 Count。这:C++ template&lt;size_t Count&gt; void test(std::array&lt;int, Count&gt;) {} int main() { test({1,2,3}); } 给出了 ``` prog.cc:45:3: 错误:没有匹配的函数用于调用 'test' test({1,2,3}); ^~~~ prog.cc:41:6:注意:候选模板被忽略:无法推断模板参数'Count' void test(std::array) {} ``
  • 是的,你是对的——出于某种奇怪的原因,我把它与 std::vector 混淆了。顺便说一句,我知道这不是你问的,但也许你可以使用 std::vector 代替数组?
  • @Claudiu Guiman,用于运行时初始化——当然。但我希望它在编译时使用constexpr 进行初始化。尽管std::initializer_list 在 C++17 中拥有所有成员 constexpr,包括 size(),但我不知道如何使其能够定义 Count 模板参数。
  • stackoverflow.com/questions/8192185/… 您可以使用两组花括号初始化 std::array: std::array<:string> strings = {{ "a", "b" } };

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


【解决方案1】:

使用建议的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,您可以在名称空间 std::experimental 内的标头 &lt;experimental/array&gt; 中找到 to_array 的实现。

【讨论】:

  • 谢谢!它以这种形式对我有用:template&lt;typename V1, typename V2, size_t Count&gt; constexpr auto makeMap2(std::pair&lt;V1, V2&gt;(&amp;&amp;a)[Count]) { return Map&lt;V1, V2, Count&gt;{ to_array(a) }; } 伟大的洞察力!周一将投入生产!
【解决方案2】:

按照 Jarod42 的建议,以递归方式,我提出了一个递归 MakeMyMap 结构,其中有一个 static func(),它接收一系列 std::pair&lt;T1, T2&gt; 参数 [注意: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,你可以定义constexprfunc()函数所以map可以是constexpr

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

你也可以验证

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

使用新的 C++17 可变参数 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(Make My Map helper)定义如下

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... } } }; }
 };

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

#include <array>
#include <string_view>
#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>
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... } } }; }
 };

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...; };

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

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

【讨论】:

  • 感谢您的建议。有趣的想法,虽然它仅限于 42 个参数(或预定数量)。
  • @paul - 不幸的是,你是对的:要求一个上限。我改进了答案以显示非递归(仅限 C++17)解决方案。在非递归方式中,您可以避免可能较低的模板递归限制。但上限要求仍然存在。
猜你喜欢
  • 2012-12-15
  • 1970-01-01
  • 2012-05-30
  • 1970-01-01
  • 1970-01-01
  • 2011-05-09
  • 1970-01-01
  • 1970-01-01
  • 2020-08-10
相关资源
最近更新 更多