I have a class that has a vector as one of member variables. Within the constructor, the vector capacity is reserved (class VecUser uses 'Test' object):
class Test {
public:
Test(uint32_t size) {
this->v.reserve(size);
std::cout << v.capacity() << std::endl; // this prints 'size'
}
vector<uint32_t>& getV() { return v; }
private:
vector<uint32_t> v;
};
class VecUser {
public:
VecUser() {}
private:
void func() {
Test* test = new Test(32); // This prints '32'
vector<uint32_t> v = test->getV();
std::cout << v.capacity() << std::endl; // This prints '0'
}
};
I think that the cout in the func() function has to print '32', not '0'.
But, after running it, it prints 0.
Why the reserved vector shows its capacity is 0?
This here
vector<uint32_t> v = test->getV();
Makes a copy. v isn't actually a reference, so even though you return one, it has to make a copy anyway. Because it is a copy, it doesn't need that same amount of reserved space. If you actually get the reference instead like this:
vector<uint32_t> &v = test->getV();
The output is 32 both times.
The copy-initialized v following vector<uint32_t> v = test->getV(); is a value copy of test->getV().
The C++ standard does not require the copying of the source vector's capacity following copy initialization, so the capacity of v is allowed to be any value subject to it being greater than or equal to the number of elements.
Related
I am confused about the output in the following program about the vec in Test. Why it's a vector with size 100 instead of 1? I thought std::vector<T> var{a} is the same as std::vector<T> var = {a}.
#include <iostream>
#include <vector>
using namespace std;
struct Value {
int a;
int b;
};
class Test {
public:
std::vector<struct Value> vec{100};
};
class Test2 {
public:
std::vector<int> vec{100};
};
int main()
{
Test test;
std::cout << "test size: " << test.vec.size() << std::endl;
Test2 test2;
std::cout << "test2 size: " << test2.vec.size();
return 0;
}
Output:
test size: 100
test2 size: 1
std::vector has a constructor with a std::initializer_list<T> argument. When using an initializer list like {100} this constructor will always take priority, if it is applicable.
For a std::vector<int> the initializer {100} is compatible with std::initializer_list<int> so that constructor will be used. It will create a vector containing the collection {100} which is a single int with the value 100.
For std::vector<Value> the initializer {100} is NOT compatible with a std::initializer_list<Value> argument. Value has no converting constructor for turning an int to a Value so you cannot construct a std::initializer_list<Value> from {100}. You can verify that this conversion is not allowed with this example. The compiler will try to take a lower priority constructor instead, and will use the constructor which initializes 100 default constructed Value.
If you add a Value::Value(int) constructor or use {{100}} as the initializer for Test2 you will find that the std::vector<Value> will now only contain a single element. In both cases, the initializer list is convertible to std::initializer_list<Value> and that constructor will now be used.
As you discovered the meaning of {100}, changes for T == int.
To answer your question briefly:
The 100 in vector<Value>{100} cannot be interpreted as a Value and therefore the size constructor takes precedence.
If you insist, {100} can be interpreted as Value, so you may need an extra curly braces, vector<Value>{ {100} }.
See the illustration here: https://godbolt.org/z/xcMT1oc5z
My advice, avoiding further discussion on legalities, is the following:
To keep the meaning across types, initialize consistently parenthesis for size-initialization and brackets for element(s), which forces you to do this:
std::vector<int> vec = std::vector<int>(100);
And in general:
std::vector<T> vec = std::vector<T>(100);
In this case 100 is always the size.
This may not be a totally helpful answer, but I decided to put a breakpoint in the class declaration for vector in the STD library.
Answer
In the definition of a vector, there are 3 ways it handles the assignment.
A struct will provide a value-construction, where as an int will be assigned as a sized range construction.
It reads std::vector<struct> vect{100}; as building a vector of length 100, while std::vector<int> vect{100}; is acting similarly vect.insert(v.end(),100);
This is based on the type of object passed in for .
For information's sake, the final option is taking a given value, and assigning it to a number of spots. So if you had 100, "x", it would put "x" into your vector 100 times.
The Journey
What I learned from this is that there's a point where your vector takes a size_type input and a _Valty&& input (which I don't know what that is yet. Will be looking it up later) and provides a construction between 3 different args.
My best guess would be that your struct is filling in for 1-args path and acts as a length declaration, while int as a native type falls into the 2-args path and acts as a value assignment.
The sizeof(Value) may == 0, while the size of an int will be 1.
Edit: I guessed 1 and 2 (or _Count == 0, and Count == 1), however I was wrong about this. It's _Count == 0 and _Count == 2. Which was very interesting.
template <class... _Valty>
_CONSTEXPR20 void _Construct_n(_CRT_GUARDOVERFLOW const size_type _Count, _Valty&&... _Val) {
// Dispatches between the three sized constructions.
// 1-arg -> value-construction, e.g. vector(5)
// 2-arg -> fill, e.g. vector(5, "meow")
// 3-arg -> sized range construction, e.g. vector{"Hello", "Fluffy", "World"}
auto& _Al = _Getal(); //////////////// For test1, _Count is 100, for test2, _Count is 1;
auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Al);
auto& _My_data = _Mypair._Myval2;
_Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data);
if (_Count != 0) {
_Buy_nonzero(_Count);
_Tidy_guard<vector> _Guard{this};
// This one happens with a struct
if constexpr (sizeof...(_Val) == 0) {
_My_data._Mylast = _Uninitialized_value_construct_n(_My_data._Myfirst, _Count, _Al);
} else
if constexpr (sizeof...(_Val) == 1) {
_STL_INTERNAL_STATIC_ASSERT(is_same_v<_Valty..., const _Ty&>);
_My_data._Mylast = _Uninitialized_fill_n(_My_data._Myfirst, _Count, _Val..., _Al);
} else
// This one happens with an int
if constexpr (sizeof...(_Val) == 2) {
_My_data._Mylast = _Uninitialized_copy(_STD forward<_Valty>(_Val)..., _My_data._Myfirst, _Al);
} else {
static_assert(_Always_false<_Ty>, "Should be unreachable");
}
_Guard._Target = nullptr;
}
_Proxy._Release();
}
What's really interesting as that it appears to be happening when the Allocator reference is assigned. I'm by no means an expert in the language, but I really wanted to figure this puzzle one! Thanks for the interesting challenge!
If you've never walked through a class definition before, I would recommend trying it out.
The problem I'm having is that I am pushing an element into a vector at initialisation when all the static variables are initialised, and then when it enters the main function the vector resets to 0, as if it has cleared. In my case the vector was in a different CPP, but this also shows the problem:
#include <iostream>
#include <vector>
struct List
{
static std::vector<int> listVector;
};
struct Foo
{
Foo()
{
std::cout << List::listVector.size() << '\n'; // Prints 0
List::listVector.push_back(1);
std::cout << List::listVector.size() << '\n'; // Prints 1
}
};
Foo fooObj;
std::vector<int> List::listVector;
int main()
{
std::cout << List::listVector.size() << '\n'; // Prints 0
List::listVector.push_back(2);
std::cout << List::listVector.size() << '\n'; // Prints 1
}
The first element I added to the vector has been lost. I know one has to be careful with initialisation order of static global variables, but I can't understand why the vector is being cleared. I'm tempted to think that when the Foo constructor runs it adds an element to the vector, but the vector hasn't been created yet, because it's written on the line after the Foo object is created. However, if this is the case, just what vector am I adding into when I do so in the constructor and it prints out a size of 1?
I'm kind of confused.
I'm tempted to think that when the Foo constructor runs it adds an element to the vector, but the vector hasn't been created yet, because it's written on the line after the Foo object is created.
Yes, that's exactly right. The fooObj constructor just accesses the memory where the vector will be constructed, but it hasn't been constructed there yet. (So the program has undefined behaviour, and must be fixed)
However, if this is the case, just what vector am I adding into when I do so in the constructor and it prints out a size of 1?
The compiler simply interprets the memory as being a valid vector, because it has no way to know otherwise. That just happens to work, because the memory that the global will be constructed in is initially zero, because the execution environment ensures that all static objects' memory is initially zero, which happens to be the same as the default-constructed state (for your implementation's definition of std::vector). The program has no way to know that the memory location containing all-zeros is not a valid vector yet.
Later the constructor for the vector runs and re-initializes the memory, to the default-constructed state.
Here is an example showing what your program does: interpreting raw memory containing all zeros as a vector, and adding elements to that phantom vector (which doesn't really exist), then actually constructing a vector in that memory. Anything added to the phantom vector is lost once the real vector is created.
#include <iostream>
#include <vector>
int main()
{
// initialize a block of memory to zero:
alignas(std::vector<int>) char memory[sizeof(std::vector<int>)] = {};
// use that memory as a vector, even though we haven't created any vector
// (this is undefined behaviour! there is no vector yet!):
std::vector<int>& vec = *reinterpret_cast<std::vector<int>*>(memory);
vec.push_back(1);
// the non-existent "phantom vector" has size 1:
std::cout << vec.size() << std::endl;
// now use "placement new" to construct an empty vector in that memory:
new (memory) std::vector<int>();
// the real vector is empty, the element in the phantom vector is lost:
std::cout << vec.size() << std::endl;
}
To fix your program you need to initialize the vector before anything refers to it (specifically, before the fooObj constructor tries to use it, by defining it before fooObj).
if you change the code to
#include <iostream>
#include <vector>
namespace List
{
static std::vector<int> listVector;
}
struct Foo
{
Foo()
{
std::cout << List::listVector.size() << '\n'; // Prints 0
List::listVector.push_back(1);
std::cout << List::listVector.size() << '\n'; // Prints 1
}
};
Foo fooObj;
//std::vector<int> List::listVector;
int main()
{
std::cout << List::listVector.size() << '\n'; // Prints 0
List::listVector.push_back(2);
std::cout << List::listVector.size() << '\n'; // Prints 1
}
it should work. I'm not in global variables (I don't use them) but I think the problem that the struct param don't make it real global. Maybe a other user can explain it ...
Edit: you can also remove the namespace, i make it so you can let the List::
Edit2: if you want to use with struct than it works with
struct List
{
std::vector<int> listVector;
};
static List GlobL;
And than use GlobL.listVector, than the struct obj ist static and not the parameter of the struct.
You have to understand that your program doesn't run from top to bottom. Every programm will start at your main() function. All the code above main() will basically get ignored if it doesn't get called in the main() function.
Questions like these should not be asked here though. It seems like you are a beginner and you should start by reading yourself more into the subject before asking questions.
If I've got a constructor with an initialization list as such:
std::vector<int> _list;
Program::Program() : _list(0)
{
}
Does this initialize all values to 0 even when the vector resizes itself?
You seem to be misunderstanding the argument of the vector constructor that you're calling. _list(0) will initialise _list to length zero, using the default value for type int, which also happens to be zero, but which is irrelevant if the vector doesn't contain any elements. It doesn't explicitly set element values to zero. Perhaps you meant to have the constructor repeats a single value a specified number of times? If so, you need to pass the desired length as the first argument, and the value to repeat for its second argument. This construction does not affect an subsequent resizing to expand the vector, which will populate the new vector elements with the default value (or a different value if you specify one as an additional argument to vector::resize).
Here's an example to illustrate, based on your code, which first initialises the vector with the value 10 repeating for length 5, and then resizes the vector to length 10.
#include <iostream>
#include <vector>
class Program
{
public:
Program() : _list(0) { }
Program(unsigned long size, int value) : _list(size, value) { }
void ResizeList(unsigned long size)
{
_list.resize(size);
}
void PrintList() const
{
std::cout << "_list = ";
for (const auto& val : _list)
{
std::cout << val << ", ";
}
std::cout << std::endl;
}
private:
std::vector<int> _list;
};
int main()
{
Program p(5, 10);
p.PrintList();
p.ResizeList(10);
p.PrintList();
return 0;
}
Output:
_list = 10, 10, 10, 10, 10,
_list = 10, 10, 10, 10, 10, 0, 0, 0, 0, 0,
If you look at the documentation for the std::vector constructor, you'll see that for constructor (3), the one you're using, you'll see that you're constructing 0 elements of type int in-place in _list. This means that you're essentially doing nothing.
When the vector is resized, the elements that space is allocated for will be uninitialized, unless you use the resize function, in which case the elements will be initialized to their default value, or a value of your choice.
For example, if your vector were empty and you did _list.resize(10);, _list would now contain 10 elements of the default-constructed type of int, which should just be 0. If you instead did something like _list.resize(10, 5);, _list would now contain 10 5s.
I hope this helped clear things up for you. If you have any follow up questions, feel free to ask.
Lets say you have something like this
#include <iostream>
#include <vector>
using namespace std;
vector<int> test()
{
vector <int> x(1000);
for (int i = 0; i < 1000; i++)
{
x[i] = 12345;
}
return x;
}
int main(int argc, const char * argv[])
{
vector<int> a = test();
return 0;
}
where within a function you create a vector and fill it with some elements (in this case I chose 12345 but they won't necessarily all be the same).
I have read that the elements of the vector are stored on the heap whereas the reference and header data are stored on the stack. In the above code, when x is returned a copy-constructor must be called, and this takes O(n) time to copy all the elements into a new vector.
However, is it possible to take advantage of the fact that all the elements already exist on the heap in order to just return something like a pointer to those elements and later just create a vector that uses that pointer in order to point to those exact same elements — thus avoiding the need to make a copy all the elements of a vector?
The compiler does this for you, freeing you up to write nice , easy-to-read code, rather than mangling your code for the sake of optimization.
When you return a value for a function, the compiler is allowed to elide the return value object. The net effect is that the compiler can just create x in the actual memory location of a.
Even if it doesn't do this (e.g. it chooses not to for some reason, or you disable it by a compiler switch), then there is still the possibility of a move.
When a move happens, the vector will just transfer ownership of the pointer from x to the return value, and then from the return value to a. This leaves x etc. as an empty vector, which is then correctly destroyed.
You could explore this by writing a test class (instead of vector<int>) which prints something out for its default constructor, copy-constructor, and move-constructor, e.g.
#include <iostream>
struct A
{
A() { std::cout << "default\n"; }
A(A const &) { std::cout << "copy\n"; }
A(A &&) { std::cout << "move\n"; }
};
A func() { A a; return a; }
int main()
{
A b (func());
}
Output with g++:
default
Output with g++ -fno-elide-constructors:
default
move
move
I'm trying to create a class which maintains a fixed size vector of unique pointers to managed objects, like so:
std::vector<std::unique_ptr<Myclass>> myVector;
The vector gets initialized like so:
myVector.resize(MAX_OBJECTS,nullptr);
Now, what I need to to, is to be able to, on request, remove one of the stored unique pointers without affecting the size of the vector.
I also need to safely add elements to the vector too, without using push_back or emplace_back.
Thanks in advance.
Edit: I want the vector to be of constant size because I want to be able to add and remove elements in constant time.
If you want a vector of fixed size, use std::array.
To remove a unique_ptr in an index, you can use std::unique_ptr::reset():
myVector[i].reset()
To add an element to a specific index (overwriting what was there before) you can use std::unique_ptr::reset() with the new pointer as parameter:
myVector[i].reset(new Myptr(myparameter));
Reading a reference may also help:
http://en.cppreference.com/w/cpp/memory/unique_ptr
http://en.cppreference.com/w/cpp/container/array
http://en.cppreference.com/w/cpp/container/vector
Looks like you want to use a std::array<> rather than forcing std::vector<> to behave like one.
As already pointed out you should use std::array if the size is fixed.
E.g like this:
std::array<std::unique_ptr<YourType>, MAX_OBJECTS> myVector;
You can then remove or add a new pointer like this.
for(auto& v : myVector)
if(v && predicate)
v.reset();// or v.reset(ptr) to set a new one
You can use STL algorithm std::remove, like this:
// all items that should be removed will be the range between removeAt and end(myVector)
auto removeAt = std::remove_if(begin(myVector), end(myVector),
ShouldRemovePredicate);
// reset all items that should be removed to be nullptr
for(auto it = removeAt; it != end(myVector); ++it)
it->reset();
In addition, if the size is known at compile-time I would suggest using std::array<unique_ptr<MyObject>, SIZE> instead of a vector. However, if SIZE is not known at compile-time your code is ok.
You could use std::array instead of a std::vector since you know the number of the elements beforehand and you could add and remove elements like the following example:
#include <iostream>
#include <memory>
#include <array>
class foo {
std::size_t id;
public:
foo() : id(0) {}
foo(std::size_t const _id) : id(_id) {}
std::size_t getid() const { return id; }
};
auto main() ->int {
// construct an array of 3 positions with `nullptr`s
std::array<std::unique_ptr<foo>, 3> arr;
// fill positions
std::unique_ptr<foo> p1(new foo(1));
arr[0] = std::move(p1);
std::unique_ptr<foo> p2(new foo(2));
arr[1] = std::move(p2);
std::unique_ptr<foo> p3(new foo(3));
arr[2] = std::move(p3);
// print array
for(auto &i : arr) if(i != nullptr) std::cout << i->getid() << " ";
std::cout << std::endl;
// reset first position (i.e., remove element at position 0)
arr[0].reset();
// print array
for(auto &i : arr) if(i != nullptr) std::cout << i->getid() << " ";
std::cout << std::endl;
return 0;
}
LIVE DEMO