c ++ std向量用现有对象初始化
c++ std vector initialize with existing objects
下面是一些包含一些已创建的 Location
对象并更新它们的代码。然后它需要构建这些对象的 std::vector
以传递给其他函数。
我构建 vector
的方式看起来更清晰,因为它是一个初始化列表并且是一行,而不是在初始化空向量后使用 3 push_back
调用。因为我们在构造时就已经知道所有要进入向量的元素。
但是,这会导致每个元素制作 2 个副本。首先,为什么要在这一行中制作两个副本?初始化列表是第一个带有副本的构造函数,然后调用向量构造函数,因此是第二个副本吗?
std::vector<Location> pointsVec {l1, l2, l3};
其次,是否有向量构造函数或其他技术来仅用一个副本初始化向量? (我想制作 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;
}
编辑:这个问题一般是针对复制成本很高的对象。向该结构添加一个字符串以证明这一点
编辑:添加样本移动构造函数
初始化器列表意味着一个副本。没有办法解决这个问题。
您可以通过在初始值设定项中写入 {std::move(l1), std::move(l2), std::move(l3)}
来用移动替换两个副本之一。请注意,由于您的 Location
定义了自定义复制构造函数,因此它实际上没有移动构造函数,并且会回退到副本。
如果要避免所有副本,则必须将元素一个一个地移动到向量中。
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));
但让我们面对现实吧:你的小 class 有 2 int
大。复制没有实际成本(并且移动复制没有优势),我的意思是字面意思:编译器可能会优化所有复制。
如果你想要向量中的所有三个 Location
对象,并且你想要它们在 L1
、L2
、L3
中,你肯定需要制作一个副本- 三个对象不能在六个地方,每个地方两次。
如果您之后在本地代码中不需要 l1
、l2
、l3
实例,您可以 移动 它们相反,通过在矢量初始化中将它们放在 std::move()
周围。然而,这是一个很大的风险,因为变量似乎仍然存在,您稍后可能会添加访问它们的代码,结果很糟糕。
更好的方法是在向量初始化调用中构造它们而不是创建临时变量:
{{1,2},{3,4},{5,6}};
.
Is there a vector constructor or another technique to initialize the vector with only 1 copy?
如果将本地对象移动到数组中,则可以从该数组构造向量,例如:
// local objects
Location locs[3]{ {1, 2}, {3, 4}, {5, 6} };
// code that updates locs ...
// construct vector
std::vector<Location> pointsVec {locs, locs+3};
另一种选择是完全摆脱局部对象,首先在 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 ...
对于这种情况,您可以结合使用 reserve
和 emplace_back
来避免由于向量元素的重新分配而导致的复制,因此您最终会得到 3 个新结构,但是没有副本:
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
下面是一些包含一些已创建的 Location
对象并更新它们的代码。然后它需要构建这些对象的 std::vector
以传递给其他函数。
我构建 vector
的方式看起来更清晰,因为它是一个初始化列表并且是一行,而不是在初始化空向量后使用 3 push_back
调用。因为我们在构造时就已经知道所有要进入向量的元素。
但是,这会导致每个元素制作 2 个副本。首先,为什么要在这一行中制作两个副本?初始化列表是第一个带有副本的构造函数,然后调用向量构造函数,因此是第二个副本吗?
std::vector<Location> pointsVec {l1, l2, l3};
其次,是否有向量构造函数或其他技术来仅用一个副本初始化向量? (我想制作 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;
}
编辑:这个问题一般是针对复制成本很高的对象。向该结构添加一个字符串以证明这一点
编辑:添加样本移动构造函数
初始化器列表意味着一个副本。没有办法解决这个问题。
您可以通过在初始值设定项中写入 {std::move(l1), std::move(l2), std::move(l3)}
来用移动替换两个副本之一。请注意,由于您的 Location
定义了自定义复制构造函数,因此它实际上没有移动构造函数,并且会回退到副本。
如果要避免所有副本,则必须将元素一个一个地移动到向量中。
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));
但让我们面对现实吧:你的小 class 有 2 int
大。复制没有实际成本(并且移动复制没有优势),我的意思是字面意思:编译器可能会优化所有复制。
如果你想要向量中的所有三个 Location
对象,并且你想要它们在 L1
、L2
、L3
中,你肯定需要制作一个副本- 三个对象不能在六个地方,每个地方两次。
如果您之后在本地代码中不需要 l1
、l2
、l3
实例,您可以 移动 它们相反,通过在矢量初始化中将它们放在 std::move()
周围。然而,这是一个很大的风险,因为变量似乎仍然存在,您稍后可能会添加访问它们的代码,结果很糟糕。
更好的方法是在向量初始化调用中构造它们而不是创建临时变量:
{{1,2},{3,4},{5,6}};
.
Is there a vector constructor or another technique to initialize the vector with only 1 copy?
如果将本地对象移动到数组中,则可以从该数组构造向量,例如:
// local objects
Location locs[3]{ {1, 2}, {3, 4}, {5, 6} };
// code that updates locs ...
// construct vector
std::vector<Location> pointsVec {locs, locs+3};
另一种选择是完全摆脱局部对象,首先在 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 ...
对于这种情况,您可以结合使用 reserve
和 emplace_back
来避免由于向量元素的重新分配而导致的复制,因此您最终会得到 3 个新结构,但是没有副本:
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