问题
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
5and then0.with an online shell, I get
5and then5
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?
- Not related to your bug, but - don't use using namespace std.
- Avoid using raw pointers when you don't really need them; and certainly don't use them to transfer ownership.
- 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