由于std::complex是一个非平凡类型,在使用GCC 8.1.1编译以下内容时需要特别注意:
complex<double>* z = new complex<double>[6];
memset(z,0,6*sizeof*z);
delete [] (z);`
产生警告
清理一个非平凡类型的对象
我的问题是,这样做实际上有潜在的危害吗?
std::memset
的行为只有在它修改的指针是指向可平凡复制类型的指针时才被定义。std::complex
保证是字面类型,但是据我所知,并没有保证它是可平凡复制类型,这意味着std::memset(z, 0, ...)
不具备可移植性。
话虽如此,std::complex
有一个数组兼容性保证,它声明了一个std::complex<T>
的存储正好是连续的两个T
,并且可以被重新解释为这样。这似乎表明std::memset
实际上是没问题的,因为它会通过这个面向数组的访问进行访问。这也可能意味着std::complex<double>
是 TriviallyCopyable,但我无法确定。
如果您想这样做,我建议您保险起见,并使用static_assert
来确认std::complex<double>
是否TriviallyCopyable:
static_assert(std::is_trivially_copyable<std::complex<double>>::value);
memset
是安全的。
std::fill
都是安全的:std::fill(z, z + 6, std::complex<double>{});
它下降优化到调用memset
,尽管在此之前有更多的指令。我建议使用std::fill
,除非您的基准测试和分析表明这些额外的指令会引起问题。
永远不要使用memset
来处理非POD类型。它们有构造函数,这是有原因的。仅仅在它们上面写一堆字节极有可能得不到所需的结果(如果确实如此,那么这些类型本身就设计得很糟糕,应该首先明确它们只是POD类型 - 或者你只是不幸地发现未定义行为在这种情况下“似乎起作用” - 当你更改优化级别、编译器或平台(或月相)时,祝你调试愉快)。
千万不要这样做。
std::complex<T>
可以合法地被解释为一个T
类型的数组,其中c[0] == c.real()
和c[1] == c.imag()
。此外,一个std::complex<T>
数组可以被解释为交替实部和虚部的T
类型数组。因此,这个特定的情况需要一些特别的考虑。 - Justinsizeof(std::complex<double>)
等于sizeof(double) * 2
,如果是这样的话,memset
就没有问题。 - NathanOliversizeof(std::complex<double>) == sizeof(double) * 2
是有保证的。 - Justin这个问题的答案是,对于符合标准的 std::complex
,在 new
之后没有必要使用 memset
。
new complex<double>[6]
将把复数初始化为 (0, 0)
,因为它调用了一个默认(非平凡)构造函数来进行零初始化。
(我认为这是一个不幸的错误。)
https://en.cppreference.com/w/cpp/numeric/complex/complex
如果发布的代码只是缺少 new
和 memset
之间的代码示例,则 std::fill
将做正确的事情。
(部分原因是特定的标准库实现内部知道如何实现 std::complex
。)
memset()
来完成这个任务?数组应该已经被正确初始化了。而且最好使用std::vector<complex<double>> z(6);
来实现你想要的效果。 - πάντα ῥεῖmemset
是完全多余的。new complex<double>[6]
在数组的每个元素上调用std::complex<double>
的默认构造函数,这相当于将实部和虚部都清零。换句话说,memset
重复了作为new[]
调用的一部分完成的工作。 - Justin