STD :: initializer_list的寿命递归使用

发布于 2025-01-25 23:40:30 字数 1181 浏览 3 评论 0原文

我正在尝试使用std :: prinitizer_list来定义和输出递归数据结构。在下面的示例中,我正在处理一个列表,其中每个元素可以是整数或同一类型列表的另一个实例。我使用中间变体类型来做到这一点,该类型可以是初始化列表或整数。

我不清楚的是std :: prinistizer_list的寿命是否足够长以支持此用例,还是我会遇到不一致的内存访问的可能性。该代码运行良好,但我担心这不能保证。我的希望是std :: prinitizer_list和任何中间的临时std :: prinitizer_list用于创建顶级列表的对象仅在顶级之后清理表达是完整的。

struct wrapped {
    bool has_list;
    int n = 0;
    std::initializer_list<wrapped> lst;

    wrapped(int n_) : has_list(false), n(n_) {}
    wrapped(std::initializer_list<wrapped> lst_) : has_list(true), lst(lst_) {}

    void output() const {
      if (!has_list) {
        std::cout << n << ' ';
      } else {
        std::cout << "[ ";
        for (auto&& x : lst)  x.output();
        std::cout << "] ";
      }
    }
  };

  void call_it(wrapped w) {
    w.output();
    std::cout << std::endl;
  }

  void call_it() {
    call_it({1});                 // [ 1 ]
    call_it({1,2, {3,4}, 5});     // [ 1 2 [ 3 4 ] 5 ]
    call_it({1,{{{{2}}}}});       // [ 1 [ [ [ [ 2 ] ] ] ] ]
  }

这是安全的还是不确定的行为?

I am trying to use std::initializer_list in order to define and output recursive data-structures. In the example below I am dealing with a list where each element can either be an integer or another instance of this same type of list. I do this with an intermediate variant type which can either be an initializer list or an integer.

What is unclear to me is whether the lifetime of the std::initializer_list will be long enough to support this use-case or if I will be experiencing the possibility of inconsistent memory access. The code runs fine, but I worry that this is not guaranteed. My hope is that the std::initializer_list and any intermediate, temporary std::initializer_list objects used to create the top-most list are only cleaned up after the top-level expression is complete.

struct wrapped {
    bool has_list;
    int n = 0;
    std::initializer_list<wrapped> lst;

    wrapped(int n_) : has_list(false), n(n_) {}
    wrapped(std::initializer_list<wrapped> lst_) : has_list(true), lst(lst_) {}

    void output() const {
      if (!has_list) {
        std::cout << n << ' ';
      } else {
        std::cout << "[ ";
        for (auto&& x : lst)  x.output();
        std::cout << "] ";
      }
    }
  };

  void call_it(wrapped w) {
    w.output();
    std::cout << std::endl;
  }

  void call_it() {
    call_it({1});                 // [ 1 ]
    call_it({1,2, {3,4}, 5});     // [ 1 2 [ 3 4 ] 5 ]
    call_it({1,{{{{2}}}}});       // [ 1 [ [ [ [ 2 ] ] ] ] ]
  }

Is this safe, or undefined behavior?

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

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

发布评论

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

评论(2

烟沫凡尘 2025-02-01 23:40:31

您所做的应该是安全的。 ISO标准第6段的第6.7.7节描述了第三个也是最后一个上下文,其中临时性在与完整表达式的末端不同的点被摧毁。脚注(35)明确表示,这适用于Intializer_list及其基本临时数组的初始化:

如果参考的完整对象,该参考的完整对象是参考的临时对象,该对象是参考限制的参考生命周期,如果通过一个人获得了引用的glvalue限制了引用的glvalue以下内容:...

...(glvalue是每7.2.1,一个评估确定对象的身份,位景或功能的表达式),然后列举条件。在(6.9)中,它特别提到:

在函数调用(7.6.1.2)中绑定到参考参数的临时对象一直存在,直到完成包含调用的全表达。

当我阅读它时,这保护了为call_it构建最终参数所需的一切,这是您的意图。

What you're doing should be safe. Section 6.7.7 of the ISO Standard, paragraph 6, describes the third and last context in which temporaries are destroyed at a different point than the end of the full expression. Footnote (35) explicitly says that this applies to the initialization of an intializer_list with its underlying temporary array:

The temporary object to which the reference is bound or the temporary object that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference if the glvalue to which the reference is bound was obtained through one of the following: ...

...(where a glvalue is, per 7.2.1, an expression whose evaluation determines the identity of an object, bit-field, or function) and then enumerates the conditions. In (6.9), it specifically mentions that:

A temporary object bound to a reference parameter in a function call (7.6.1.2) persists until the completion of the full-expression containing the call.

As I read it, this protects everything needed to build up the final argument to the top call to call_it, which is what you intend.

清醇 2025-02-01 23:40:30

据我所知,该程序的行为不确定。

成员声明std :: prinitizer_list&lt; trapp&gt; lst;需要完成类型,因此将隐式实例化std :: prinitizer_list&lt; trapped&gt;

此时,包装是一种不完整的类型。根据 [res.on.functions] /2.5 ,如果没有说明特定的例外,请实例化具有不完整类型的标准库模板,因为模板参数是未定义的。

我在

另请参见活动 lwg问题2493

As far as I can tell the program has undefined behavior.

The member declaration std::initializer_list<wrapped> lst; requires the type to be complete and hence will implicitly instantiate std::initializer_list<wrapped>.

At this point wrapped is an incomplete type. According to [res.on.functions]/2.5, if no specific exception is stated, instantiating a standard library template with an incomplete type as template argument is undefined.

I don't see any such exception in [support.initlist].

See also active LWG issue 2493.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文