Why Does 'Std::Vector<Int> B{2};' Create a 1-Element Vector, and Not a 2-Element One

Why does 'std::vector int b{2};' create a 1-element vector, and not a 2-element one?

Yes, this behaviour is intended, according to §13.3.1.7 Initialization by list-initialization

When objects of non-aggregate class type T are list-initialized
(8.5.4), overload resolution selects the constructor in two phases:

— Initially, the candidate functions are the initializer-list
constructors (8.5.4) of the class T and the argument list consists of
the initializer list as a single argument.

— If no viable
initializer-list constructor is found, overload resolution is
performed again, where the candidate functions are all the
constructors of the class T and the argument list consists of the
elements of the initializer list.

As to "the whole purpose of uniform intialization"... "Uniform initialization" is a marketing term, and not a very good description. The standard has all the usual forms of initialization plus list-initialization, but no "uniform initialization". List initialization is not meant to be the ultimate form of initialization, it's just another tool in the utility belt.

Strange behaviour of vector in cpp

vector<int> result = {0}; creates a vector with one element so you aren't allowed to access anything but result[0]. An alternative would be to create the vector with as many elements that you need, A.size() + B.size() - 1.

Example:

#include <iostream>
#include <vector>

std::vector<int> multp(const std::vector<int>& A, const std::vector<int>& B) {
std::vector<int> result(A.size() + B.size() - 1); // correct size

/* this is not needed, they will be 0 by default
for(int i = 0; i < A.size() + B.size(); i++) {
result[i] = 0;
}
*/

for(size_t i = 0; i < A.size(); i++) {
for(size_t j = 0; j < B.size(); j++) {
result[i + j] += A[i] * B[j];
}
}

return result;
}

int main() {
std::vector<int> A = {1, 1};
std::vector<int> B = {1, 1, 1};
std::vector<int> result = multp(A, B);

for(int resval : result) { // a convenient range based for-loop
std::cout << resval << ' ';
}
std::cout << '\n';
}

How is vector vector int heavier than vector pair int,int ?

Each vector is a single contiguous area of memory, dynamically allocated.

Let's say that you have 1000 values you'll be working with.

std::vector<std::pair<int, int>>

This gets you a single, contiguous block of memory, for 2000 integers.

std::vector<std::vector<int>>

This gets you a single contiguous block of memory for 1000 vectors.

Each one of those 1000 std::vectors gets you another contiguous block of memory for just two integers.

So, instead of one single contiguous block of memory, for this data structure, it will consist of 1001 blocks of memory scattered all over. You have no guarantees, whatsoever, that all those blocks of memory will be contiguous, one after another.

Each dynamic memory allocation comes at a cost. The cost is fairly small but it adds up very, very quickly. A single penny is easily ignored. A thousand pennies should be enough to get you a cup of coffee at Starbucks.

Furthermore, modern CPUs are very, very good at accessing contiguous blocks of memory. Iterating over a single contiguous block of memory to add up two thousand ints will be much, much faster than doing the same over a thousand disjointed sections of memory.

C++ free error with vector instantiated with fill constructor

What you claim is "the full output" isn't the full output of the program you show, since that contains an output of values.size() which should be 1. And that is also what leads to the problem you're having: You have a vector of a single element, and access it out of bounds, which leads to undefined behavior.

If you initialize std::vector using curly-braces it will be using the std::initializer_list constructor of std::vector.

If you want the vector to be initialized with (2*k+1) * (2*k+1) number of elements, you need to use parentheses instead:

std::vector<int> values((2*k+1) * (2*k+1));

Understanding syntax of function parameter: vector vector int A[]

Just as int foo(char str[]) is a function that takes a (c-style) array of characters, so int foo(vector<vector<int>> B[]) takes an array of vectors of vectors ... of integers. This means that it's three-dimensional data, requiring 3 indices to access the elements (fundamental data type; in this case, int), like B[i][j][k] = 5. Without the extra [] in the API it'd be two-dimensional data: a vector of vectors.

Note that int foo(char str[]) is equivalent to int foo(char str[5]) which is equivalent to int foo(char * str).
In C we usually add the [] to a function declaration to imply that we expect to receive an array of those elements; while * is often used when we expect at most one element. Likewise, adding the number [5] is basically just a comment to the user of the code that they expect 5 elements, but the compiler won't enforce this. These conventions carry over to C++ when we use these c-style arrays ... which is rare.

With c-style arrays there's either going to be a maximum array size in the comments somewhere; or, more commonly, it's provided as an input. That may be what the first argument of the function is supposed to represent.

I agree with KungPhoo here that this API looks suspiciously bad. I'd expect bugs/bad performance just because the choices seem very amateurish. The c-style array means the function can't know where the end of the c-style array is - but the vectors mean that we give up some of the (niche) benefits of c-style simplicity (especially because they're nested!). It seems to be getting the worst of both worlds. But, perhaps, there may be a very niche justification for the API.

Is there multiple ways to cin vector elements?

vector<int> q2(2) makes a vector q2 that has 2 default-constructed int elements in it. You don't need to push_back() elements, because it makes 2 elements for you when it is constructed.

cin >> q2[b] merely edits the int elements that already exist in the vector.

What is the easiest way to initialize a std::vector with hardcoded elements?

One method would be to use the array to initialize the vector

static const int arr[] = {16,2,77,29};
vector<int> vec (arr, arr + sizeof(arr) / sizeof(arr[0]) );

Why can't I initialize this std::vector with an l-value?

TL;DR

The problem is not specific/limited to std::vector but instead is a consequence of the rule quoted below from the standard.


Let's see on case by case basis what is happening and why do we get the mentioned narrowing conversion error/warning when using lvalue.

Case 1

Here we consider:

int lvalue = 6; // lvalue is not a constant expression 

//---------------------------v------------------->constant expression so works fine
std::vector<int*> myvector { 6 };
std::vector<int*> myvector{ lvalue };
//--------------------------^^^^^^--------------->not a constant expression so doesn't work

First note that std::vector<int*> does not have an initializer list constructor that takes an initializer list of int.

So in this case the size_t count ctor will be used. Now let's see the reason for getting narrowing conversion error/warning.

The reason we get an error/warning when using the variable named lvalue while not when using a prvalue int is because in the former case lvalue is not a constant expression and so we have a narrowing conversion. This can be seen from dcl.init.list#7 which states:

A narrowing conversion is an implicit conversion

  • from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression whose value after integral promotions will fit into the target type.

(emphasis mine)

This means that the conversion from lvalue(which is an lvalue expression) which is of type int to size_t parameter of the vector's std::vector::vector(size_t, /*other parameters*/) ctor, is a narrowing conversion. But the conversion from prvalue int 6 to the size_t parameter of the vector's std::vector::vector(size_t, /*other parameters*/) is not a narrowing conversion.

To prove that this is indeed the case, lets look at some examples:

Example 1

int main()
{
//----------------v---->no warning as constant expression
std::size_t a{1};

int i = 1;
//----------------v---->warning here i is not a constant expression
std::size_t b{i};

constexpr int j = 1;
//----------------v---->no warning here as j is a constexpr expression
std::size_t c{j};
return 0;
}

Example 2

struct Custom 
{
Custom(std::size_t)
{

}
};
int main()
{
//-----------v---->constant expression
Custom c{3}; //no warning/error here as there is no narrowing conversion

int i = 3; //not a constant expressoion

//-----------v---->not a constant expression and so we get warning/error
Custom d{i}; //warning here of narrowing conversion here


constexpr int j = 3; //constant expression

//-----------v------>no warning here as j is a constant expression and so there is no narrowing conversion
Custom e{j};
return 0;
}

Demo



Case 2

Here we consider:

//------------v-------------------------->note the int here instead of int* unlike case 1 
std::vector<int> myvector{num_elements};//this uses constructor initializer list ctor

In this case there is a initializer list ctor available for std::vector<int> and it will be preferred over the size_t count constructor as we've used braces {} here instead of parenthesis (). And so a vector of size 1 will be created. More details at Why is the std::initializer_list constructor preferred when using a braced initializer list?.


On the other hand, when we use:

std::vector<int> myvector(num_elements); //this uses size_t ctor

Here the size_t ctor of std::vector will be used as the initializer list ctor is not even viable in this case as we've used parenthesis (). And so a vector of size 6 will be created. You can confirm this using the example given below:

struct Custom 
{

Custom(std::size_t)
{
std::cout<<"size t"<<std::endl;
}
Custom(std::initializer_list<int>)
{
std::cout<<"initializer_list ctor"<<std::endl;
}
};
int main()
{
Custom c(3); //uses size_t ctor, as the initializer_list ctor is not viable
return 0;
}


Related Topics



Leave a reply



Submit