递归嵌套初始化列表采用变体

发布于 2025-02-11 03:52:10 字数 1429 浏览 2 评论 0 原文

我希望一个对象 obj 可以从Pairs的 initializer_list 初始化。但是,这对的第二个值是bool,int和 obj 的变体。 GCC报告了我猜找到正确的构造函数的麻烦。我可以为以下代码递归制作这项工作吗?

#include <utility>
#include <string>
#include <variant>

struct obj;

using val = std::variant<int, bool, obj>;

struct obj
{
    obj(std::initializer_list<std::pair<std::string, val>> init) {
        
    }
};

int main()
{
    obj O = { {"level1_1", true }, { "level1_2", 1 }, { {"level2_1", 2}, {"level2_2", true}}};
}

GCC 12.1不明白:

<source>: In function 'int main()':
<source>:57:93: error: could not convert '{{"level1_1", true}, {"level1_2", 1}, {{"level2_1", 2}, {"level2_2", true}}}' from '<brace-enclosed initializer list>' to 'obj'
   57 |     obj O = { {"level1_1", true }, { "level1_2", 1 }, { {"level2_1", 2}, {"level2_2", true}}};
      |                                                                                             ^
      |                                                                                             |
      |                     

有什么暗示我需要更改的内容?

编辑:似乎使用初始化器列表的方法可能不合适。也许使用模板存在解决方案,这也是值得赞赏的。我知道从 nlohmanns json library (请参阅“ JSON as json as First-class数据类型”)) 。此外,解决方案应在初始化过程中不使用堆库来实现。

I want an object obj to be initialized from an initializer_list of pairs. However, the second value of the pair is a variant of bool, int and again the obj. gcc reports trouble finding the right constructors I guess. Can I make this work recursively for the following code?

#include <utility>
#include <string>
#include <variant>

struct obj;

using val = std::variant<int, bool, obj>;

struct obj
{
    obj(std::initializer_list<std::pair<std::string, val>> init) {
        
    }
};

int main()
{
    obj O = { {"level1_1", true }, { "level1_2", 1 }, { {"level2_1", 2}, {"level2_2", true}}};
}

gcc 12.1 doesn't get it:

<source>: In function 'int main()':
<source>:57:93: error: could not convert '{{"level1_1", true}, {"level1_2", 1}, {{"level2_1", 2}, {"level2_2", true}}}' from '<brace-enclosed initializer list>' to 'obj'
   57 |     obj O = { {"level1_1", true }, { "level1_2", 1 }, { {"level2_1", 2}, {"level2_2", true}}};
      |                                                                                             ^
      |                                                                                             |
      |                     

Any hints on what I need to change?

EDIT: It seems that the approach using initializer lists is probably not suitable. Maybe there exists a solution using templates which is also appreciated. I know it is possible from nlohmanns json library (see section "JSON as first-class data type"). Also the solution should get by without using heap allocations during the initialization process.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

情绪失控 2025-02-18 03:52:10

您可以使用模板元编程在编译时创建特定类型。

template <int Level>
struct obj {
    using value =
        std::initializer_list<
            std::pair<
                std::string_view,
                std::variant<bool, int, typename obj<Level-1>::value>>>;
};

template <>
struct obj<1> {
    using value
        = std::initializer_list<
            std::pair<
                std::string_view,
                std::variant<bool, int>>>;
};

int main() {
    obj<2>::value o{
        { "level1_1", true },  // (string, bool)
        { "level1_2", 1 },  // (string, int)
        { "level2", obj<1>::value{  // (string, obj)
                { "level2_1", 2 },
                { "level2_2", true }}}};
}

在上面的代码中, obj&lt; 2&gt; :: value 将被编译为:

std::initializer_list<  // obj<2>::value
    std::pair<
        std::string_view,
        std::variant<
            bool,
            int,
            std::initializer_list<  // obj<1>::value
                std::pair<
                    std::string_view,
                    std::variant<bool, int>>>>>>

我相信不应该进行堆分配,因为您仅使用基本类型(bool,int), std :: string_view , std :: pair std :: variant (应该像结构一样行为), std:prinisterizer_list 。检查此处用于演示上面示例中的所有 o 值在堆栈上。

You could use template metaprogramming to create specific types at compile time.

template <int Level>
struct obj {
    using value =
        std::initializer_list<
            std::pair<
                std::string_view,
                std::variant<bool, int, typename obj<Level-1>::value>>>;
};

template <>
struct obj<1> {
    using value
        = std::initializer_list<
            std::pair<
                std::string_view,
                std::variant<bool, int>>>;
};

int main() {
    obj<2>::value o{
        { "level1_1", true },  // (string, bool)
        { "level1_2", 1 },  // (string, int)
        { "level2", obj<1>::value{  // (string, obj)
                { "level2_1", 2 },
                { "level2_2", true }}}};
}

In the code above, obj<2>::value would be compiled to:

std::initializer_list<  // obj<2>::value
    std::pair<
        std::string_view,
        std::variant<
            bool,
            int,
            std::initializer_list<  // obj<1>::value
                std::pair<
                    std::string_view,
                    std::variant<bool, int>>>>>>

I believe no heap allocations should be made, since you are only making use of basic types (bool, int), std::string_view, std::pair and std::variant (should behave like a struct), and std:initializer_list. Check here for a demo showing all the o values in the example above are on the stack.

给妤﹃绝世温柔 2025-02-18 03:52:10

正如Igor指出的那样,如果没有对您的类型或构造函数进行任何更改,您的代码可以通过为您的子对象指定的显式类型进行编译:

obj O = { 
  { "level1_1", true },
  { "level1_2", 1    },
  { "level2",  obj {
      { "level2_1", 2    },
      { "level2_2", true },
    }
  },
};

我相信问题可能源于 std :: Pair ,,以及包含不完整类型的变体。我们可以用我们自己的自定义类型替换 std :: pair&lt; ...&gt; 缺乏任何内部存储的类型:

struct obj
{
  using mapped_type = std::variant<int, bool, obj>;

  struct kv_pair
  {
    kv_pair(const std::string &k, int  v) { }
    kv_pair(const std::string &k, bool v) { }
    kv_pair(const std::string &k, const obj &v) { }
  };
  
  obj(std::initializer_list<kv_pair> entries) { }
};

int main(void)
{
  obj _ = {
    { "level1_1", true },
    { "level1_2", 1    },
    { "level2", {
        { "level2_1", 2     },
        { "level2_2", false },
      },
    },
  };
  return 0;
}

这样,我们得到了您所需的语法,但缺乏任何实现。我的猜测是,问题在于 std :: Pair std :: variant 结合使用。我们可以通过拥有 kv_pair 来恢复我们的存储空间,而是通过成对的字符串和指针的便利包装器,即为

struct kv_pair : public std::pair<std::string, std::shared_ptr<mapped_type>>
{
  kv_pair(const std::string &k, int        v) : pair(k, std::make_shared<mapped_type>(v) { }
  kv_pair(const std::string &k, bool       v) : pair(k, std::make_shared<mapped_type>(v) { }
  kv_pair(const std::string &k, const obj &v) : pair(k, std::make_shared<mapped_type>(v) { }
};

完整而完整的实现:

struct obj
{
  using mapped_type = std::variant<int, bool, obj>;

  struct kv_pair : public std::pair<std::string, std::shared_ptr<mapped_type>>
  {
    kv_pair(const std::string &k, int        v) : pair(k, std::make_shared<mapped_type>(v)) { }
    kv_pair(const std::string &k, bool       v) : pair(k, std::make_shared<mapped_type>(v)) { }
    kv_pair(const std::string &k, const obj &v) : pair(k, std::make_shared<mapped_type>(v)) { }
  };

  obj(std::initializer_list<kv_pair> entries) 
  {
    for (auto &[k, v]: entries)
    {
      _entries.emplace(k, *v); 
    }
  }

protected:
  std::map<std::string, mapped_type> _entries;
};

int main(void)
{
  obj _ = {
    { "level1_1", true },
    { "level1_2", 1    },
    { "level2", {
        { "level2_1", 2     },
        { "level2_2", false },
      },
    },
  };
  return 0;
}

As Igor noted, without any changes to your type or constructor, your code can compile fine with an explicit type specified for your subobject:

obj O = { 
  { "level1_1", true },
  { "level1_2", 1    },
  { "level2",  obj {
      { "level2_1", 2    },
      { "level2_2", true },
    }
  },
};

I believe the problem may originate with the particular combination of std::pair, along with a variant that contains an incomplete type. We can observe this by replacing std::pair<...> with our own custom type that lacks any internal storage:

struct obj
{
  using mapped_type = std::variant<int, bool, obj>;

  struct kv_pair
  {
    kv_pair(const std::string &k, int  v) { }
    kv_pair(const std::string &k, bool v) { }
    kv_pair(const std::string &k, const obj &v) { }
  };
  
  obj(std::initializer_list<kv_pair> entries) { }
};

int main(void)
{
  obj _ = {
    { "level1_1", true },
    { "level1_2", 1    },
    { "level2", {
        { "level2_1", 2     },
        { "level2_2", false },
      },
    },
  };
  return 0;
}

With this, we get your desired syntax, but lack any implementation. My guess here is that the problem lies with std::pair in combination with std::variant. We can get back our storage by having kv_pair instead be a convenience wrapper over pairs of strings and pointers, i.e.

struct kv_pair : public std::pair<std::string, std::shared_ptr<mapped_type>>
{
  kv_pair(const std::string &k, int        v) : pair(k, std::make_shared<mapped_type>(v) { }
  kv_pair(const std::string &k, bool       v) : pair(k, std::make_shared<mapped_type>(v) { }
  kv_pair(const std::string &k, const obj &v) : pair(k, std::make_shared<mapped_type>(v) { }
};

So for a full and complete implementation:

struct obj
{
  using mapped_type = std::variant<int, bool, obj>;

  struct kv_pair : public std::pair<std::string, std::shared_ptr<mapped_type>>
  {
    kv_pair(const std::string &k, int        v) : pair(k, std::make_shared<mapped_type>(v)) { }
    kv_pair(const std::string &k, bool       v) : pair(k, std::make_shared<mapped_type>(v)) { }
    kv_pair(const std::string &k, const obj &v) : pair(k, std::make_shared<mapped_type>(v)) { }
  };

  obj(std::initializer_list<kv_pair> entries) 
  {
    for (auto &[k, v]: entries)
    {
      _entries.emplace(k, *v); 
    }
  }

protected:
  std::map<std::string, mapped_type> _entries;
};

int main(void)
{
  obj _ = {
    { "level1_1", true },
    { "level1_2", 1    },
    { "level2", {
        { "level2_1", 2     },
        { "level2_2", false },
      },
    },
  };
  return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文