【问题标题】:memset and a dynamic array of std::complex<double>memset 和 std::complex<double> 的动态数组
【发布时间】:2018-08-02 16:09:47
【问题描述】:

由于 std::complex 是一个非平凡的类型,使用 GCC 8.1.1 编译以下内容

complex<double>* z = new complex<double>[6];
memset(z,0,6*sizeof*z);
delete [] (z);`

产生警告

清除非平凡类型的对象

我的问题是,这样做真的有任何潜在的危害吗?

【问题讨论】:

  • 你为什么认为你需要memset()呢?数组应该已经正确初始化。也最好简单地使用std::vector&lt;complex&lt;double&gt;&gt; z(6); 来实现你想要的。
  • 我不会这样用。这只是一种引发警告的方式。
  • 请注意,在这种情况下,memset 是完全多余的。 new complex&lt;double&gt;[6] 在数组的每个元素上调用std::complex&lt;double&gt; 的默认构造函数,这相当于将实部和虚部都归零。换句话说,memset 复制了作为new[] 调用的一部分所做的工作。
  • 是的,看我上面的评论。

标签: c++ memset


【解决方案1】:

std::memset 的行为仅在它正在修改的指针是指向 TriviallyCopyable 类型的指针时才被定义。 std::complex 保证是 LiteralType,但据我所知,不保证是 TriviallyCopyable,这意味着 std::memset(z, 0, ...) 不可移植.

也就是说,std::complex 有一个array-compatibility guarantee,它表明std::complex&lt;T&gt; 的存储正好是两个连续的Ts,可以这样重新解释。这似乎表明std::memset 实际上很好,因为它将通过这种面向数组的访问进行访问。这也可能暗示std::complex&lt;double&gt; TriviallyCopyable,但我无法确定。

如果您希望这样做,我建议您注意安全,static_asserting std::complex&lt;double&gt;TriviallyCopyable

static_assert(std::is_trivially_copyable<std::complex<double>>::value);

如果该断言成立,则可以保证memset 是安全的。


在任何一种情况下,使用std::fill 都是安全的:

std::fill(z, z + 6, std::complex<double>{});

optimizes down 调用memset,尽管在它之前还有一些指令。我建议使用std::fill,除非您的基准测试和分析表明这些额外的指令会导致问题。

【讨论】:

  • 它确实针对 GCC 和 clang 进行了优化,但当我在该页面上选择 X64 MSVC 时显然不是。
【解决方案2】:

永远,永远,永远memset 非 POD 类型。他们有构造函数是有原因的。仅仅在它们上面写一堆字节不太可能给出想要的结果(如果确实如此,那么类型本身的设计很糟糕,因为它们显然应该首先是 POD - 或者你只是不走运未定义的行为似乎在这种情况下工作 - 在您更改优化级别、编译器或平台(或月相)后,如果它不起作用,调试它会很有趣)。 p>

不要这样做。

【讨论】:

  • 有趣的是std::complex&lt;T&gt; 可以合法地解释为Tc[0] == c.real()c[1] == c.imag() 的数组。此外,std::complex&lt;T&gt; 的数组可以解释为实部和虚部交替的T 的数组。所以这个具体案例需要一些特殊的考虑
  • 与@Justin 一起发表评论,因为这些要求很可能sizeof(std::complex&lt;double&gt;) 等于sizeof(double) * 2,如果是,则memset 没有理由成为问题。
  • @Justin 即使你可以在这种情况下做到这一点,为什么要冒险呢?尝试做这样的事情太脆弱且容易出错。而且它甚至可能不会以任何真正重要的方式更快。
  • @NathanOliver 不只是最有可能; sizeof(std::complex&lt;double&gt;) == sizeof(double) * 2 保证
  • @Zdenek Prusa 谁应该编写编译器特定的代码。应该编写符合标准的代码,保证在不同的编译器中都能以相同的方式工作。
【解决方案3】:

这个问题的答案是,对于符合标准的std::complex,在new 之后不需要memset

new complex&lt;double&gt;[6] 会将复合体初始化为(0, 0),因为它调用了一个初始化为零的默认(非平凡)构造函数。 (不幸的是,我认为这是一个错误。) https://en.cppreference.com/w/cpp/numeric/complex/complex

如果发布的代码只是newmemset 之间缺少代码的示例,那么std::fill 将做正确的事情。 (部分是因为特定的标准库实现在内部知道std::complex 是如何实现的。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 1970-01-01
    • 1970-01-01
    • 2021-12-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多