struct PhiBlock
{
int64_t bsize; // block size
vector<int64_t> ind; // 0/1 to track [pmin(y) > pb]
fenwick_tree phi_sum; // partial sums
PhiBlock (int64_t bsize, int64_t a):
phi_sum(ind)
{
this->bsize = bsize;
ind.resize(bsize, 1);
phi_sum = fenwick_tree(ind);
}
// ...
My data structure:
struct fenwick_tree
{
size_t len; // 0-based len
std::vector<int64_t> t; // 1-based tree, indexes [1:len]
fenwick_tree(std::vector<int64_t> const &a)
{
len = a.size();
t.assign(len + 1, 0);
for (size_t i = 0; i < len; ++i)
add_to(i, a[i]);
}
// ...
};
If I don't include the member initialization list for PhiBlock
, GCC complains about no matching function call to fenwick_tree::fenwick_tree()
. So, here's what I think is happening:
fenwick_tree
has no default constructor, so I have to use member initialization phi_sum(ind)
, where ind
is default constructed as a vector to be empty.
ind
is resized.
A new fenwick_tree
is constructed using ind
, then phi_sum
is set to that with however operator=
is implemented.
Is this correct? Must phi_sum
be constructed twice? If I modeled my fenwick_tree
off of the standard library containers, then it should have a default constructor that does nothing - then I wouldn't need a member initialization list, right?
Update: Here is the gcc output https://godbolt.org/z/5Y7Y9o15W
It's hard for me to see what's going on, but as far as I can tell based on the calls:
call operator new(unsigned long)
is for fenwick_tree.len
call std::vector<long, std::allocator<long> >::_M_fill_insert(__gnu_cxx::__normal_iterator<long*, std::vector<long, std::allocator<long> > >, unsigned long, long const&)
is to ind.resize
call operator new(unsigned long)
and call memset
are for fenwick_tree.t
Without -O2, fenwick_tree
is clearly getting constructed twice. I would guess with -O2 fenwick_tree
is not constructed twice.
Your understanding is basically correct. Since fenwick_tree
does not have a default constructor, the phi_sum
member MUST be constructed using the member initialization list of PhiBlock
's constructor.
However, class members are initialized in the order that they are declared in the class (regardless of the order that they appear in the constructor's member initialization list). And, any non-defaulted member can be (and should be) initialized in the member initialization list.
So, because the ind
member is declared before the phi_sum
member, you can initialize ind
first via the member initialization list, using the vector::vector(size_type count, const T& value)
constructor. And then, you can initialize phi_sum
afterwards using the now-populated ind
member, eg:
struct PhiBlock
{
int64_t bsize; // block size
vector<int64_t> ind; // 0/1 to track [pmin(y) > pb]
fenwick_tree phi_sum; // partial sums
PhiBlock (int64_t bsize, int64_t a):
bsize(bsize), ind(bsize, 1), phi_sum(ind)
{
}
// ...
};