Strict aliasing rule

后端 未结 2 383
甜味超标
甜味超标 2021-01-18 09:58

I\'m reading notes about reinterpret_cast and it\'s aliasing rules ( http://en.cppreference.com/w/cpp/language/reinterpret_cast ).

I wrote that code:



        
2条回答
  •  广开言路
    2021-01-18 10:47

    Yeah, it's invalid, but not because you're converting a char* to an A*: it's because you are not obtaining a A* that actually points to an A* and, as you've identified, none of the type aliasing options fit.

    You'd need something like this:

    #include 
    #include 
    
    struct A
    {
      int t;
    };
    
    char *buf = new char[sizeof(A)];
    
    A* ptr = new (buf) A;
    ptr->t = 1;
    
    // Also valid, because points to an actual constructed A!
    A *ptr2 = reinterpret_cast(buf);
    std::cout << ptr2->t;
    

    Now type aliasing doesn't come into it at all (though keep reading because there's more to do!).

    • (live demo with -Wstrict-aliasing=2)

    In reality, this is not enough. We must also consider alignment. Though the above code may appear to work, to be fully safe and whatnot you will need to placement-new into a properly-aligned region of storage, rather than just a casual block of chars.

    The standard library (since C++11) gives us std::aligned_storage to do this:

    using Storage = std::aligned_storage::type;
    auto* buf = new Storage;
    

    Or, if you don't need to dynamically allocate it, just:

    Storage data;
    

    Then, do your placement-new:

    new (buf) A();
    // or: new(&data) A();
    

    And to use it:

    auto ptr = reinterpret_cast(buf);
    // or: auto ptr = reinterpret_cast(&data);
    

    All in it looks like this:

    #include 
    #include 
    #include 
    
    struct A
    {
      int t;
    };
    
    int main()
    {
        using Storage = std::aligned_storage::type;
    
        auto* buf = new Storage;
        A* ptr = new(buf) A();
    
        ptr->t = 1;
    
        // Also valid, because points to an actual constructed A!
        A* ptr2 = reinterpret_cast(buf);
        std::cout << ptr2->t;
    }
    

    (live demo)

    Even then, since C++17 this is somewhat more complicated; see the relevant cppreference pages for more information and pay attention to std::launder.

    Of course, this whole thing appears contrived because you only want one A and therefore don't need array form; in fact, you'd just create a bog-standard A in the first place. But, assuming buf is actually larger in reality and you're creating an allocator or something similar, this makes some sense.

提交回复
热议问题