【问题标题】:Is aliasing an array of T to an array of std::complex<T> legal?将 T 数组别名为 std::complex<T> 数组是否合法?
【发布时间】:2025-12-29 06:45:09
【问题描述】:

我知道强类型别名规则。但是,cppreference 指出

实现不能声明额外的非静态数据成员,这些成员会占用与实部和虚部不相交的存储空间,并且必须确保类模板特化不包含任何填充。实现还必须确保对数组访问的优化考虑到指向 value_type 的指针可能为 std::complex 特化或其数组起别名的可能性。

这个要求是否允许像下面这样的代码是合法的,即使它不一定是道德的?

std::size_t numDoubles = 100;
double* a = (double*) std::malloc(numDoubles * sizeof(double));
std::complex<double>* b = reinterpret_cast<std::complex<double>*>(a);
DoSomethingWith(b[1]);

如果使用 new[] 生成双精度数组,答案会改变吗?

XY 问题说明:我正在尝试对可能不存在的第三方库提供的分配器进行抽象;该分配器返回一个 void*;我试图避免库中存在的细节泄漏到标题中。所以我的结构如下:

// foo.h
namespace impl {
    void* Allocate(std::size_t numBytes);
}

template<typename T>
T* Allocate(std::size_t num) {
    static_assert(std::is_pod_v<T>);
    return static_cast<T*>(Allocate(num * sizeof(T)));
}


// foo.cpp
#if LIBRARY_PRESENT

void* impl::Allocate(std::size_t numBytes) { return LibraryAllocate(numBytes); }

#else

void* impl::Allocate(std::size_t numBytes) { return std::malloc(numBytes); }

#endif

不幸的是,std::complex 不是 POD,因为它有一个不平凡的默认构造函数。我希望我可以忽略这个问题。

【问题讨论】:

    标签: c++ language-lawyer strict-aliasing


    【解决方案1】:

    此要求是否允许以下​​代码合法

    即使您直接使用a 而不是对complex 进行强制转换,该代码也不严格合法。 C++ 对象模型不允许您只获取一些内存,将其转换为一种类型,然后假装对象存在于该内存中。无论类型如何(字节类型之外)都是如此。

    该代码从不创建double 的数组,因此您无法访问内存,就好像double 的数组在那里一样。您也无法访问complex&lt;double&gt;,就好像它就在那里一样。

    如果使用new[] 生成双精度数组,答案是否会改变?

    仅在存在double 的合法数组的意义上。那里仍然没有complex&lt;double&gt;

    std::complex&lt;T&gt; 提供的规则是you can access its members as though it were an array of two Ts。这意味着您可以访问任何两个Ts 的数组,就好像它是一个std::complex&lt;T&gt;

    【讨论】:

    • 很公平。看来我必须在 malloc 的内存中放置新的位置?
    • @JamesPicone:嗯,这就是问题所在:您是在分配内存还是在创建对象? C++ 标准库分配器模型为这些单独的任务提供了单独的函数:allocator&lt;T&gt;::allocate 分配适合 T 的内存,而 allocator&lt;T&gt;::create 在此类存储中创建对象。
    • 除了在其中创建对象之外,您对分配的内存无能为力。为了尝试解释抽象,我试图获取某种类型 T 的数组,通常是 double 或 std::complex,但我用于数组的内存可能来自 CUDA 分配函数这将它放在一个特殊的地方并返回一个 void*。而且因为我试图在 .cpp 文件中执行此操作,所以我没有类型信息,这使得 std::allocator::allocate 或道德等价物无法真正发挥作用。
    【解决方案2】:

    所以标准部分[complex.numbers]p4 是这样说的:

    如果 z 是 cv complex 类型的左值,则:

    • 表达式 reinterpret_cast(z) 应格式正确,
    • reinterpret_cast(z)[0] 应指定 z 的实部,并且
    • reinterpret_cast(z)[1] 应指定 z 的虚部。

    此外,如果 a 是 cv complex* 类型的表达式,并且 表达式 a[i] 对于整数表达式 i 是明确定义的,那么:

    • reinterpret_cast(a)[2*i] 应指定 a[i] 的实部,并且
    • reinterpret_cast(a)[2*i + 1] 应指定 a[i] 的虚部。

    反之,它不会对 strict aliasing rule 产生异常,因此看起来通过 doublestd::complex&lt;double&gt; 进行别名是无效的。

    使用 new 不会改变分析。

    【讨论】:

    • 我认为格式化程序在您上面的答案中吞噬了 &lt;&gt; 字符中的一些内容。