5 ms·
C++ has std::vector, which is one level of abstraction above a slice; you push to a vector, and maybe the backing slice changes, but it's still the same vector.
by edflsafoiewq 5y ago
C++ has std::vector, which is one level of abstraction above a slice; you push to a vector, and maybe the backing slice changes, but it's still the same vector.
Go is unusual in not having a equivalent of vector.
- pjmlp 5y agoNot really, if reallocation takes place. So if you got a pointer to a vector element, it now points to garbage.
- simiones 5y agoI guess the difference is this: C++: std::vector<int> v {1, 2, 3}; void foo(std::vector<int> *ref) { ref.push_back(4); } foo(&v); //v[3] == 4 is true here Go: v := []int{1, 2, 3} func foo(ref *[]int) { append(ref, 4) } foo(&v) //v[3] == 4 may or may not be true here. Pointers to elements in the vector do indeed have the same problems both in Go and C++ (except for memory safety).
- pjmlp 5y agoAh ok, although for the audience not versed in C++ the correct code is, void foo(std::vector<int> *ref) { ref->push_back(4); } foo(&v); or void foo(std::vector<int> &ref) { ref.push_back(4); } foo(v);
- simiones 5y agoOops, right, bit rusty on C++...
- tialaramex 5y ago> //v[3] == 4 is true here Note that C++ std::vector has an operator overload so this is actually just calling the method named operator[] on the object v and that method returns you a reference to the object in the backing array if in fact it is a suitable size (no checks are made). In particular if foo doesn't for any reason extend the vector then our program now has Undefined Behaviour. This is not merely the C syntactic sugar array subscript operation v[3] == *(v+3) as the vector is not necessarily just a backing array pointer and some magic. In contrast Go is really offering array syntax for this slice, that's the built-in array subscript operation and it cares whether v[3] exists when you try to compare it to 4.
- simiones 5y agoI'm not sure I understand how this is relevant. In C++, arr[non_existent_index] is UB both if arr is an std::vector and if it is a C-style array. In Go it's a runtime error instead. Sure, std::vector::operator[]() is not just syntax sugar for *(v+i), but I don't think any of this is relevant for the discussion at hand, unless I'm missing something.
- leaveyou 5y agoappend(ref, 4) // error: first argument to append must be slice append(*ref, 4) // error: append(*ref, 4) evaluated but not used this is the correct version and it also removes the incertitude: *ref = append(*ref, 4) https://play.golang.org/p/-xDqaxvqWhm https://play.golang.org/p/-xDqaxvqWhm
- simiones 5y agoYes, I made a significant error in forgetting what slice variables actually represent. In my example, even fixing the compilation errors (with `_ = append(*ref, 4)` ), v[3] would always be an array index out of bounds error, since v itself always points to just the first 3 elements of the array, regardless of resizing. This is another significant difference between slices and C++ vectors. A more interesting example showing that resizing can be observed (getting rid of the pointer to the slice, since it's not useful anyway): https://play.golang.org/p/UJ-t63bKyJJ https://play.golang.org/p/UJ-t63bKyJJ
- simiones 5y agoToo late to edit, but I made a significant mistake in the Go code: v[3] would always be a runtime error unless we explicitly modify *ref inside the function (ref = append(ref, 4), in which case v[3] == 4 would always be true). This happens regardless of resizing, since append() at best modifies the array that *ref/v points to, but it does not modify *ref/v itself; and slices in Go have a pointer to an underlying storage AND a start and end index into that storage (multiple slices can point to different parts of the same storage). Created here an example that shows how this interacts with resizing: https://play.golang.org/p/UJ-t63bKyJJ https://play.golang.org/p/UJ-t63bKyJJ
- Thorrez 5y agoYes, std::vector and slice are different. My point is that a slice is similar to std::span. With both slice and std::span, = does a shallow copy of just 2 or 3 pointers. With std::vector = does a deep copy of every element, you have 2 distinct backing arrays. Go forces you to use copy() and append() and rely on the garbage collector to make a slice fill the roll of std::vector. IMO it leads to some confusing code.