behavior of c++ code changes depending on the compiler

狂风中的少年 提交于 2020-06-17 15:49:27

问题


I’m really new to c and c++

I tried to experiment with structs to create a list-like structure that can contain float and lists, basically.

This code compiles, but it behaves differently depending on the compiler:

  • with the latest version of visual studio community, it outputs 5 and then 0.

  • with an online shell, I get 5 and then 5

The one I would want to get is the second one when the vector gets passed through the function.

here is the code:

#include <iostream>
#include <vector>

using namespace std;

struct Box {
    Box(char t) {
        type = t;
    }
    union Value {
        float number;
        vector<Box>* elements;
    };
    Value value;
    char type;
};

Box newBox() {
    Box aBox('l');

    vector<Box> newVec;
    newVec.assign(5, Box('n'));


    aBox.value.elements = &newVec;
    cout << aBox.value.elements->size() << "\n";
    return aBox;

}

int main()
{
    Box test = newBox();
    cout << test.value.elements->size();  // this is not always working nicely
}


Where does that come from?

Is there something wrong with my code?

And is there a better way to create this kind of structure?


回答1:


On this line:

aBox.value.elements = &newVec;

you are storing the address of a local variable. When you return from the newBox function, that variables dies, and then accessing that memory via the pointer invokes undefined behavior.




回答2:


Is there something wrong with my code?

@cigien's answer explains what's wrong.

And is there a better way to create this kind of structure?

  1. Not related to your bug, but - don't use using namespace std.
  2. Avoid using raw pointers when you don't really need them; and certainly don't use them to transfer ownership.
  3. Use vocabulary types such as std::variant and std::optional instead of raw unions.

So, Try this:

#include <iostream>
#include <variant>
#include <vector>

struct Box {
    struct Item { float value; };
    using Boxes = std::vector<Box>;
    struct Empty {};

    Box(char t) : type(t) { }

    auto& item() &            { return std::get<Item>(contents); }
    const auto& item() const  { return std::get<Item>(contents); }

    auto& boxes()             { return std::get<Boxes>(contents); }
    const auto& boxes() const { return std::get<Boxes>(contents); }

    std::variant<Empty, Item, Boxes> contents { Empty{} };
    char type;
};

Box newBox() {
    Box aBox('l');

    Box::Boxes subBoxes;
    subBoxes.assign(5, Box('n'));

    aBox.contents = std::move(subBoxes);
    std::cout << "aBox has " << test.boxes().size() << " sub-boxes.\n";
    return aBox;
}

int main()
{
    Box test = newBox();
    std::cout << test.boxes().size() << '\n';
}

This is better - no pointers, and it's clearer what you can put in a box. However, note that making assumptions about the current active kind of element ina variant is usually a bad idea, so try to avoid std::get() in favor of std:visit() usually, to be on the safe side. Also, perhaps a method telling you how many items there in the box (in the shallow and the deep sense) could make sense. Read up on std::variant if you want to go in that direction.



来源:https://stackoverflow.com/questions/62334326/behavior-of-c-code-changes-depending-on-the-compiler

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!