Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call not default object constructor for each object uniquely when creating a vector of objects

Tags:

c++

vector

I am trying to create a vector of objects and would like the not default constructor to be called uniquely for each object in the vector. I have an simplified example below.

In my example I have an object with two constructors, default (no args) and non-default (1 arg). When I initialized a vector of size 10 using the default constructor (v1), the constructor gets called 10 times (as can be seen by the random numbers). However, when I try to initialize a vector with the objects non-default constructor (v2), the object constructor is called once and this object is copied to the remaining elements in the vector (x is no longer many different random numbers).

Is it possible to initialize a vector of N objects so that each objects non-default constructor is called for each object?

Example Code:

#include <vector>
#include <iostream>
#include <cstdlib>

struct Obj {
    int x;

    Obj() {
        x = rand() % 5;
    }

    Obj(int max_x) {
        x = rand() % max_x;
    }
};

int main() {
    std::vector<Obj> v1(10);     // Default constructor
    std::vector<Obj> v2(10, 5);  // Non-Default Constructor

    for(int i=0; i<10; ++i) {
        std::cout << v1[i].x << ", " << v2[i].x << std::endl;
    }
}

Output:

3, 2
1, 2
2, 2
0, 2
3, 2
0, 2
1, 2
2, 2
4, 2
1, 2

Solution

The following function can be used to return an object vector where the not default constructor is called of each object.

template <typename T, typename ... Args>  std::vector<T> InitVector(const int n, Args ... args) {
     std::vector<T> vec;
     for(int i = 0; i < n; ++i) {
         vec.emplace_back(args ...);
     }
     return vec;
}
like image 202
lotuspaperboy Avatar asked Jan 02 '23 06:01

lotuspaperboy


2 Answers

Here're two workarounds.

  1. Use list initialization.

    std::vector<Obj> v2{5, 5, 5, ...}; 
    
  2. Use emplace_back to insert the elements later.

    std::vector<Obj> v2;
    v2.reserve(10);
    for (int i=0; i<10; ++i) {
        v2.emplace_back(5);
    }
    
like image 125
songyuanyao Avatar answered Jan 04 '23 20:01

songyuanyao


I'm not quite sure whether this could be a solution but you could overload the copy constructor to do the intended construction.

I tried the idea with overloading the copy constructor:

#include <vector>
#include <iostream>
#include <cstdlib>

struct Obj {
    const int max_x;
    int x;

    Obj(): Obj(5) { }

    Obj(int max_x): max_x(max_x), x(rand() % max_x) { }

    Obj(const Obj &obj): Obj(obj.max_x) { }
};

int main() {
    std::vector<Obj> v1(10);     // Default constructor
    std::vector<Obj> v2(10, 5);  // Non-Default Constructor

    for(int i=0; i<10; ++i) {
        std::cout << v1[i].x << ", " << v2[i].x << std::endl;
    }
}

Output:

3, 2
1, 0
2, 4
0, 3
3, 1
0, 0
1, 1
2, 2
4, 1
1, 1

Live Demo on coliru

It's better but the draw back is the additional member in struct Obj. :-(

like image 21
Scheff's Cat Avatar answered Jan 04 '23 19:01

Scheff's Cat