Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

in-place vector construction from initialization list (for class with constructor arguments) [duplicate]

Possible Duplicate:
Can I list-initialize a vector of move-only type?

Edit 1: Please consider a re-open vote: My question emphasize in-place construction. Move construction is an alternative but not what this questions is about. Thanks for the answers!

Edit 2: Since I can't answer this question (It got closed) I post my own suggestion here. The following is not as good as the answers I accepted, but may be useful for others. At least only the move constructor is called:

std::vector<A2> vec;
{
  std::array<A2,3> numbers{{{2,3},{5,6},{7,8}}};
  vec.reserve(numbers.size());
  for (auto &v: numbers) vec.emplace_back(std::move(v)) ;
}

Original post:

When thinking about the answer to this question: Initialization of classes within an STL array of vectors I found that I could not find a way to get in-place construction of the vector from an initialization list. What am I missing?

Now trying to be more clear, I would like this (perfectly correct) initialization

std::vector<A2> k{{2,3},{4,5},{8,9}};

to have an effect more similar to this:

  std::vector<A2> k2;
  k2.reserve(3);
  k2.emplace_back(2,3);
  k2.emplace_back(4,5);
  k2.emplace_back(8,9);

However, in the first case the copy constructor is called for A2 on a temporary while inserting. Is there a way to avoid that? What does the standard say?

I desperately tried

std::vector<A2> k{{2,3},{4,5},std::move(A2{8,9})};

but that generates an additional call to the move constructor, something I also did not expect. I just wanted to explicitly hint that A2 is a temporary, something I had thought was implied.

Full example:

#include <vector>
#include <iostream>

struct A2 {
  int mk;
  int mj;
  A2(int k,int j) : mk(k),mj(j) {
    std::cout << "     constr for "<<this<< ":"<< mk<<std::endl;
  }
  A2(const A2& a2) {
    mk=a2.mk;
    mj=a2.mj;    
    std::cout << "copy constr for "<<this<< ":" << mk<<std::endl;
  }
  A2(A2&& a2) noexcept  {
    mk=std::move(a2.mk);
    mj=std::move(a2.mj);
    std::cout << "move constr for "<<this<< ":"<< mk<<std::endl;
  }
};

struct Ano {
  Ano() {
    std::cout << "     constr for "<<this <<std::endl;
  }
  Ano(const Ano& ano) {
    std::cout << "copy constr for "<<this<<std::endl;
  }
  Ano(Ano&& ano) noexcept  {
    std::cout << "move constr for "<<this<<std::endl;
  }
};


int main (){
  // here both constructor and copy constructor is called:
  std::vector<A2> k{{2,3},{4,5},std::move(A2{8,9})};

  std::cout << "......"<<std::endl;
  std::vector<A2> k2;
  k2.reserve(3);
  // here (naturally) only constructor is called:
  k2.emplace_back(2,3);
  k2.emplace_back(4,5);
  k2.emplace_back(8,9);

  std::cout << "......"<<std::endl;  
  // here only constructor is called:
  std::vector<Ano> anos(3);

}

Output:

     constr for 0xbf9fdf18:2
     constr for 0xbf9fdf20:4
     constr for 0xbf9fdf0c:8
move constr for 0xbf9fdf28:8
copy constr for 0x90ed008:2
copy constr for 0x90ed010:4
copy constr for 0x90ed018:8
......
     constr for 0x90ed028:2
     constr for 0x90ed030:4
     constr for 0x90ed038:8
......
     constr for 0x90ed048
     constr for 0x90ed049
     constr for 0x90ed04a
like image 730
Johan Lundberg Avatar asked Oct 30 '12 22:10

Johan Lundberg


1 Answers

Construction of an object via std::initializer_list is no different than constructing an object from any other object. The std::initializer_list is not a mystical, phantasmal construct; it is a living, breathing C++ object (albeit a temporary one). As such, it obeys all the rules of regular living, breathing C++ objects.

Aggregate initialization can effectively elide the copy/moves because it's aggregate initialization, a purely compile-time construct. std::vector is many things; an aggregate and a purely compile-time construct are not among them. So, in order for it to initialize itself from what it is given, it must execute actual C++ code, not compile-time stuff. It must iterate over each element of the initializer_list and either copy those values or move them. And the latter is not possible, since std::initializer_list doesn't provide non-const access to its members.

Initializer list initialization is meant to look like aggregate initialization, not perform like it. That's the cost of having a runtime, dynamic abstraction like std::vector.

like image 56
Nicol Bolas Avatar answered Sep 28 '22 01:09

Nicol Bolas