【问题标题】:Lose reference to std::variant object after changing its type更改类型后失去对 std::variant 对象的引用
【发布时间】:2020-01-26 14:44:25
【问题描述】:

请注意,可以通过运行下面的 sn-p 来重现该问题(我使用 gcc 9.1 的 wandbox)

所以我有两个自定义类型(NormalSpecial)的std::array(为简单起见,大小为 2)Normal 被指定为第一个类型,因此在类构造时,数组是默认值- 用Normal 对象构建。我更改了数组第一个元素的一些内部数据成员并将其打印出来。看起来不错。

现在我想将数组的第二个元素设置为Special 对象。根据本教程,我尝试通过分配新值和使用 emplace 来做到这一点 (https://www.bfilipek.com/2018/06/variant.html#changing-the-values)

但是,当我尝试更改第二个对象的内部数据成员(现在键入 Special)时,似乎我没有对原始数组中的对象进行操作。打印结果显示构造的默认值(在这种情况下为 0)我是使用 std::variant 的新手,所以我不知道为什么会这样。如何获得对数组中最近类型更改变体对象的实际引用?

#include <iostream>
#include <memory>
#include <cstring>
#include <array>
#include <variant>

struct Normal {
    struct Header {
        std::array<uint8_t, 2> reserved;
    };
    Normal() : frame{0}, payload{reinterpret_cast<uint8_t*>(frame + sizeof(Header))} {}
    constexpr static auto LENGTH = 10;
    uint8_t frame[LENGTH];
    uint8_t* payload;
};

struct Special {
    struct Header {
        std::array<uint8_t, 3> reserved;
    };
    Special() : frame{0}, payload{reinterpret_cast<uint8_t*>(frame + sizeof(Header))} {}
    constexpr static auto LENGTH = 11;
    uint8_t frame[LENGTH];
    uint8_t* payload;
};

std::array<std::variant<Normal, Special>, 2> handlers;
Normal* normal_handler;
Special* special_handler;

int main() {
    auto& nh = std::get<Normal>(handlers[0]);
    memset(nh.payload, 3, 3);
    normal_handler = &nh;

    handlers[1].emplace<1>(Special{});
    auto& sh = std::get<Special>(handlers[1]);
    memset(sh.payload, 4 ,4);
    // memset(std::get<Special>(handlers[1]).payload, 4, 4);
    special_handler = &sh;

    for (int i = 0; i < 10; i++) {
        // Expect 3 bytes from 3rd bytes = 3
        std::cout << (int) normal_handler->frame[i] << " ";
    }

    std::cout << std::endl;

    for (int i = 0; i < 11; i++) {
        // Expect 4 bytes from 4th bytes = 4
        std::cout << (int) special_handler->frame[i] << " ";
        // std::cout << (int) std::get<Special>(handlers[1]).frame[i] << " ";
    }

}

【问题讨论】:

    标签: c++ arrays variant


    【解决方案1】:

    您的问题与std::variant 无关,以下代码显示相同的行为:

    #include <iostream>
    #include <memory>
    #include <cstring>
    
    struct Special {
        struct Header {
            std::array<uint8_t, 3> reserved;
        };
        Special() : frame{0}, payload{reinterpret_cast<uint8_t*>(frame + sizeof(Header))} {}
        constexpr static auto LENGTH = 11;
        uint8_t frame[LENGTH];
        uint8_t* payload;
    };
    
    int main() {
    
        Special s1;
        s1 = Special{};
        memset(s1.payload, 4 ,4);
    
        for (int i = 0; i < 11; i++) {
            // Expect 4 bytes from 4th bytes = 4
            std::cout << (int) s1.frame[i] << " ";
        }
    }
    

    这一行:

        s1 = Special{};
    

    创建一个临时的Special 对象,然后将其分配给s1。默认的复制和移动构造函数将在临时中将s1.payload 设置为payload 的值。因此s1.payload 是临时对象中指向frame 的悬空指针,因此您的其余代码具有未定义的行为。

    最简单的解决方法是将payload 成员更改为函数:

    #include <iostream>
    #include <memory>
    #include <cstring>
    
    struct Special {
        struct Header {
            std::array<uint8_t, 3> reserved;
        };
        Special() : frame{0} {}
        constexpr static auto LENGTH = 11;
        uint8_t frame[LENGTH];
        uint8_t* payload() { return &frame[sizeof(Header)]; }
    };
    
    int main() {
    
        Special s1;
        s1 = Special{};
        memset(s1.payload(), 4 ,4);
    
        for (int i = 0; i < 11; i++) {
            // Expect 4 bytes from 4th bytes = 4
            std::cout << (int) s1.frame[i] << " ";
        }
    
    }
    

    【讨论】:

    • 很好的答案,谢谢。我陷入了变种,没有看到更根本的问题。
    猜你喜欢
    • 2022-09-21
    • 2019-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-26
    • 2018-04-09
    • 2022-07-21
    相关资源
    最近更新 更多