简体   繁体   English

c++ 标准矢量用现有对象初始化

[英]c++ std vector initialize with existing objects

Below is some code that contains some already created Location objects and updates them.下面是一些代码,其中包含一些已创建的Location对象并更新它们。 Then it needs to construct a std::vector of those objects to pass to other functions.然后它需要构建这些对象的std::vector以传递给其他函数。

The way I construct the vector looks cleaner as it is a initializer list and is one line, instead of using 3 push_back calls after initializing an empty vector.我构建vector的方式看起来更清晰,因为它是一个初始化列表并且是一行,而不是在初始化一个空向量后使用 3 个push_back调用。 Since we know all the elements that are going in to the vector already at construction time.因为我们知道在构建时已经进入向量的所有元素。

However, this leads to 2 copies being made per element.但是,这会导致每个元素制作 2 个副本。 Firstly, why are there two copies being made in this line?首先,为什么要在这一行中制作两个副本? Is the initializer list first constructor with copies, and then the vector constructor is called, therefore a second copy?初始化列表是第一个带有副本的构造函数,然后调用向量构造函数,因此是第二个副本吗? std::vector<Location> pointsVec {l1, l2, l3};

And secondly, is there a vector constructor or another technique to initialize the vector with only 1 copy?其次,是否有向量构造函数或其他技术来仅使用一个副本来初始化向量? (I want to make exactly 1 copy as I still want to use the local objects) (我想制作 1 个副本,因为我仍然想使用本地对象)

struct Location 
{
    Location(int x, int y, std::string frame)
    : x(x)
    , y(y)
    , frame(std::move(frame))
    {
        std::cout << "ctor" << std::endl;
    }

    Location(const Location & other)
    : x(other.x)
    , y(other.y)
    , frame(other.frame)
    {
        std::cout << "copy ctor" << std::endl;
    }
    
    Location(Location && other)
    : x(std::move(other.x))
    , y(std::move(other.y))
    , frame(std::move(other.frame))
    {
        std::cout << "move ctor" << std::endl;
    }

    int x;
    int y;
    std::string frame;
};

int main ()
{
    // local objects 
    Location l1 {1, 2, "local"};
    Location l2 {3, 4, "global"};
    Location l3 {5, 6, "local"};
    
    // code that updates l1, l2, l3
    // .
    // .
    // .

    // construct vector 
    std::vector<Location> pointsVec {l1, l2, l3}; // 2 copies per element 

    std::vector<Location> pointsVec1;
    pointsVec1.push_back(l1);
    pointsVec1.push_back(l2);
    pointsVec1.push_back(l3); // 1 copy per element 
    
    return 0;
}

edit: this question was in general for objects that are expensive to copy.编辑:这个问题一般是针对那些复制起来很昂贵的对象。 adding a string to this struct to demonstrate that point向此结构添加一个字符串以证明这一点

edit: adding sample move ctor编辑:添加样本移动构造函数

Is there a vector constructor or another technique to initialize the vector with only 1 copy?是否有向量构造函数或其他技术来仅使用一个副本来初始化向量?

If you move the local objects into an array, you can construct the vector from that array, eg:如果将本地对象移动到数组中,则可以从该数组构造向量,例如:

// local objects 
Location locs[3]{ {1, 2}, {3, 4}, {5, 6} };
    
// code that updates locs ...

// construct vector 
std::vector<Location> pointsVec {locs, locs+3};

Online Demo在线演示

Another option would be to simply get rid of the local objects altogether, construct them inside the vector to begin with, and then just refer to those elements, eg:另一种选择是简单地完全摆脱局部对象,首先在vector中构造它们,然后只引用这些元素,例如:

// construct vector 
std::vector<Location> pointsVec{ {1, 2}, {3, 4}, {5, 6} };

// local objects 
Location &l1 = pointsVec[0];
Location &l2 = pointsVec[1];
Location &l3 = pointsVec[2];
    
// code that updates l1, l2, l3 ...

Initializer lists imply a copy.初始化列表意味着一个副本。 There's no way around this.没有办法解决这个问题。

You can replace one of the two copies with a move, by writing {std::move(l1), std::move(l2), std::move(l3)} in the initializer.您可以通过在初始化程序中编写{std::move(l1), std::move(l2), std::move(l3)}来用移动替换两个副本之一。 Note that, since your Location defines a custom copy constructor, it doesn't actually have a move constructor and will fall back to the copy.请注意,由于您的Location定义了自定义复制构造函数,因此它实际上没有移动构造函数,并且会回退到副本。

If you want to avoid all copies, you instead have to move the elements into the vector one by one.如果要避免所有副本,则必须将元素一个一个地移动到向量中。

std::vector<Location> pointsVec;
pointsVec.reserve(3); // or else you get more copies/moves when the vector rellocates
pointsVec.push_back(std::move(l1));
pointsVec.push_back(std::move(l2));
pointsVec.push_back(std::move(l3));

But let's face it: your little class is 2 int s large.但让我们面对现实:你的小 class 是 2 int s 大。 There is no real cost to the copies (and no advantage in moving over copying), and I mean that literally: chances are, the compiler will just optimize all of the copying away.复制没有实际成本(并且移动复制没有优势),我的意思是字面意思:编译器可能只会优化所有复制。

If you want all three Location objects inside the vector, and you want them in L1 , L2 , L3 , you definitely need to make a copy - three objects cannot be in six places, twice each.如果你想要向量中的所有三个Location对象,并且你想要它们在L1L2L3中,你肯定需要制作一个副本 - 三个对象不能在六个地方,每个地方两次。

If you don't need the l1 , l2 , l3 instances in your local code afterwards, you could move them instead, by putting std::move() around them in the vector initialization.如果之后您的本地代码中不需要l1l2l3实例,您可以通过在向量初始化中将它们放在std::move()周围来移动它们。 This is a big risk, however, because the variables seem to be still there and you might later add code that accesses them, with ugly consequences.然而,这是一个很大的风险,因为变量似乎仍然存在,您稍后可能会添加访问它们的代码,从而带来丑陋的后果。

The better approach would be to construct them right in the vector initialization call instead of making temporary variables: {{1,2},{3,4},{5,6}};更好的方法是在向量初始化调用中构造它们,而不是创建临时变量: {{1,2},{3,4},{5,6}}; . .

For this case, you could use a combination of reserve , to avoid copies due to reallocation of the vector elements, and emplace_back , so you would end up with 3 new constructions, but no copies:对于这种情况,您可以结合使用reserveemplace_back来避免由于向量元素的重新分配而产生的副本,因此您最终会得到 3 个新结构,但没有副本:

[Demo] [演示]

    std::vector<Location> pointsVec1;
    pointsVec1.reserve(3);
    pointsVec1.emplace_back(l1.x, l1.y);
    pointsVec1.emplace_back(l2.x, l2.y);
    pointsVec1.emplace_back(l3.x, l3.y); // 1 ctor per element 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM