C++ 11:将结构数组重新解释为struct的成员数组

Alb*_*ria 11 c++ reinterpret-cast c++11

请考虑以下类型:

struct S
{
    char v;
};
Run Code Online (Sandbox Code Playgroud)

给定一个数组const S,是否有可能以符合标准的方式将其重新解释为一个数组,const char其元素对应v于每个原始数组元素的成员值,反之亦然?例如:

const S a1[] = { {'a'}, {'4'}, {'2'}, {'\0'} };
const char* a2 = reinterpret_cast< const char* >(a1);

for (int i = 0; i < 4; ++i)
    std::cout << std::boolalpha << (a1[i].v == a2[i]) << ' ';
Run Code Online (Sandbox Code Playgroud)

上面的代码是否可移植,是否可以打印true true true true?如果没有,有没有其他方法来实现这一目标?

显然,可以创建一个新数组并使用v原始数组的每个元素的成员对其进行初始化,但整个想法是避免创建新数组.

MSa*_*ers 8

琐碎的,没有 - struct可能有填充.而这种平​​局打破了任何对阵列的重新解释.

  • @набиячлэвэли你仍然会打破别名规则.当编译器得到更明智的优化时,许多打破别名规则并且运行良好多年的实际代码开始破坏.假设它会起作用是一个可怕的想法. (2认同)

Che*_*Alf 6

形式上struct可能有填充,使其大小大于1.

也就是说,正式地你不能reinterpret_cast拥有完全可移植的代码,除了只有一个项目的¹an数组.

但对于在实践,若干年前有人问是否有任何现在编译器在默认情况下会给出sizeof(T) > 1struct T{ char x; };.我还没有看到任何例子.所以在实践中,只要static_assert大小为1,就不用担心这static_assert会在某些系统上失败.

也就是说,

S const a1[] = { {'a'}, {'4'}, {'2'}, {'\0'} };
static_assert( sizeof( S ) == 1, "!" );

char const* const a2 = reinterpret_cast<char const*>( a1 );

for( int i = 0; i < 4; ++i )
{
    assert( a1[i].v == a2[i] );
}
Run Code Online (Sandbox Code Playgroud)

由于可以以索引具有未定义行为的方式解释C++ 14及更高版本的标准,基于对"数组"的特殊解释来引用某些原始数组,可能会将此代码编写为更加笨拙并且详细但有保证的有效方式:

// I do not recommend this, but it's one way to avoid problems with some compiler that's
// based on an unreasonable, impractical interpretation of the C++14 standard.
#include <assert.h>
#include <new>

auto main() -> int
{
    struct S
    {
        char v;
    };

    int const compiler_specific_overhead    = 0;    // Redefine per compiler.
    // With value 0 for the overhead the internal workings here, what happens
    // in the machine code, is the same as /without/ this verbose work-around
    // for one impractical interpretation of the standard.
    int const n = 4;
    static_assert( sizeof( S ) == 1, "!" );
    char storage[n + compiler_specific_overhead]; 
    S* const a1 = ::new( storage ) S[n];
    assert( (void*)a1 == storage + compiler_specific_overhead );

    for( int i = 0; i < n; ++i ) { a1[i].v = "a42"[i]; }    //  Whatever

    // Here a2 points to items of the original `char` array, hence no indexing
    // UB even with impractical interpretation of the C++14 standard.
    // Note that the indexing-UB-free code from this point, is exactly the same
    // source code as the first code example that some claim has indexing UB.
    char const* const a2 = reinterpret_cast<char const*>( a1 );

    for( int i = 0; i < n; ++i )
    {
        assert( a1[i].v == a2[i] );
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:
¹标准保证在开始时没有填充struct.

  • "不便携"并不意味着UB.@TC是正确的,UB来自指针算术而不是使用`reinterpret_cast`,见[expr.add] p6. (2认同)