After many trials I still do not understand how to properly take advantage of the move semantics in order to not copy the result of the operation and just use the pointer, or std::move, to "exchange" the data pointed to. This will be very usefull to speed-up more complicated functions like f(g(),h(i(l,m),n(),p(q())) The objective is to have:
经过多次试验后,我仍然不明白如何正确利用移动语义,以便不复制操作的结果,只需使用指针或std :: move来“交换”指向的数据。这对于加速更复杂的函数非常有用,例如f(g(),h(i(l,m),n(),p(q()))目标是:
t3={2,4,6};
t1={}; // empty
While executing the code below the output is:
执行输出下面的代码时是:
t3={2,4,6};
t1={1,2,3};
Code:
namespace MTensor {
typedef std::vector<double> Tensor1DType;
class Tensor1D {
private:
//std::shared_ptr<Tensor1DType> data = std::make_shared<Tensor1DType>();
Tensor1DType * data = new Tensor1DType;
public:
Tensor1D() {
};
Tensor1D(const Tensor1D& other) {
for(int i=0;i<other.data->size();i++) {
data->push_back(other.data->at(i));
}
}
Tensor1D(Tensor1D&& other) : data(std::move(other.data)) {
other.data = nullptr;
}
~Tensor1D() {
delete data;
};
int size() {
return data->size();
};
void insert(double value) {
data->push_back(value);
}
void insert(const std::initializer_list<double>& valuesList) {
for(auto value : valuesList) {
data->push_back(value);
}
}
double operator() (int i) {
if(i>data->size()) {
std::cout << "index must be within vector dimension" << std::endl;
exit(1);
}
return data->at(i);
}
Tensor1D& operator=(Tensor1D&& other) {
if (this == &other){
return *this;
}
data = other.data;
other.data = nullptr;
return *this;
}
void printTensor(Tensor1DType info) {
for(int i=0;i<info.size();i++) {
std::cout << info.at(i) << "," << std::endl;
}
}
void printTensor() {
for(int i=0;i<data->size();i++) {
std::cout << data->at(i) << "," << std::endl;
}
}
};
} // end of namespace MTensor
In file main.cpp:
在main.cpp文件中:
MTensor::Tensor1D scalarProduct1D(MTensor::Tensor1D t1, double scalar) {
MTensor::Tensor1D tensor;
for(int i=0;i<t1.size();++i) {
tensor.insert(t1(i) * scalar);
}
//return std::move(tensor);
return tensor;
}
int main() {
MTensor::Tensor1D t1;
t1.insert({1,2,3});
std::cout << "t1:" << std::endl;
t1.printTensor();
MTensor::Tensor1D t3(scalarProduct1D(t1,2));
std::cout << "t3:" << std::endl;
t3.printTensor();
std::cout << "t1:" << std::endl;
t1.printTensor();
return 0;
}
2 个解决方案
#1
0
Your use of new
is a red flag, especially on a std::vector
.
你对new的使用是一个红旗,特别是在std :: vector上。
std::vector
s support move semantics natively. They are a memory management class. Manual memory management of a memory management class is a BIG red flag.
std :: vectors支持本机移动语义。它们是一个内存管理类。内存管理类的手动内存管理是一个很大的红旗。
Follow the rule of 0. =default
your move constructor, move assignment, copy constructor, destructor and copy assignment. Remove the *
from the vector. Don't allocate it. Replace data->
with data.
遵循规则0. =默认移动构造函数,移动赋值,复制构造函数,析构函数和复制赋值。从向量中删除*。不要分配它。用数据替换data->。
The second thing you should do is change:
你应该做的第二件事是改变:
MTensor::Tensor1D scalarProduct1D(MTensor::Tensor1D t1, double scalar) {
As it stands you take the first argument by value. That is great.
就目前而言,您可以按值获取第一个参数。那样太好了。
But once you take it by value, you should reuse it! Return t1
instead of creating a new temporary and returning it.
但是一旦你按价值看待它,你应该重复使用它!返回t1而不是创建一个新临时并返回它。
For that to be efficient, you will want to have a way to modify a tensor in-place.
为了提高效率,您需要有一种方法来就地修改张量。
void set(int i, double v) {
if(i>data->size()) {
std::cout << "index must be within vector dimension" << std::endl;
exit(1);
}
data.at(i) = v;
}
which gives us:
这给了我们:
MTensor::Tensor1D scalarProduct1D(MTensor::Tensor1D t1, double scalar) {
for(int i=0;i<t1.size();++i) {
ts.set(i, t1(i) * scalar);
}
return t1; // implicitly moved
}
We are now getting close.
我们现在越来越近了。
The final thing you have to do is this:
你要做的最后一件事是:
MTensor::Tensor1D t3(scalarProduct1D(std::move(t1),2));
to move the t1
into the scalarProduct1D
.
将t1移动到scalarProduct1D中。
A final problem with your code is that you use at
and you check bounds. at
's purpose is to check bounds. If you use at
, don't check bounds (do so with a try/catch). If you check bounds, use []
.
您的代码的最后一个问题是您使用at并检查边界。的目的是检查界限。如果使用at,请不要检查边界(使用try / catch执行此操作)。如果检查边界,请使用[]。
End result:
typedef std::vector<double> Tensor1DType;
class Tensor1D {
private:
//std::shared_ptr<Tensor1DType> data = std::make_shared<Tensor1DType>();
Tensor1DType data;
public:
Tensor1D() {};
Tensor1D(const Tensor1D& other)=default;
Tensor1D(Tensor1D&& other)=default;
~Tensor1D()=default;
Tensor1D& operator=(Tensor1D&& other)=default;
Tensor1D& operator=(Tensor1D const& other)=default;
Tensor1D(const std::initializer_list<double>& valuesList) {
insert(valuesList);
}
int size() const {
return data.size();
};
void insert(double value) {
data.push_back(value);
}
void insert(const std::initializer_list<double>& valuesList) {
data.insert( data.end(), valuesList.begin(), valuesList.end() );
}
double operator() (int i) const {
if(i>data.size()) {
std::cout << "index must be within vector dimension" << std::endl;
exit(1);
}
return data[i];
}
void set(int i, double v) {
if(i>data->size()) {
std::cout << "index must be within vector dimension" << std::endl;
exit(1);
}
data.at(i) = v;
}
static void printTensor(Tensor1DType const& info) {
for(double e : info) {
std::cout << e << "," << std::endl;
}
}
void printTensor() const {
printTensor(data);
}
};
MTensor::Tensor1D scalarProduct1D(MTensor::Tensor1D t1, double scalar) {
for(int i=0;i<t1.size();++i) {
t1.set(i, t1(i) * scalar);
}
return t1;
}
int main() {
MTensor::Tensor1D t1 = {1,2,3};
std::cout << "t1:" << std::endl;
t1.printTensor();
MTensor::Tensor1D t3(scalarProduct1D(std::move(t1),2));
std::cout << "t3:" << std::endl;
t3.printTensor();
std::cout << "t1:" << std::endl;
t1.printTensor();
return 0;
}
with a few other minor fixes (like using range-for, DRY, etc).
还有一些其他小修正(比如使用range-for,DRY等)。
#2
0
You need to move t1
when calling scalarProduct1D
, otherwise you'll make a copy:
调用scalarProduct1D时需要移动t1,否则你将复制:
MTensor::Tensor1D t3(scalarProduct1D(std::move(t1),2));
You need to explicitly use std::move
because t1
is an lvalue expression.
您需要显式使用std :: move,因为t1是左值表达式。
Note that you'll have to fix your printing functions to avoid dereferencing nullptr
if you want accessing the moved-from object to be a valid operation. I instead suggest to avoid making method invocation on moved-from objects valid as it requires additional checks and doesn't follow the idea of "this object has been moved, now it's in an invalid state".
请注意,如果要将移动的对象作为有效操作,则必须修复打印函数以避免取消引用nullptr。我反而建议避免对移动对象进行方法调用,因为它需要额外的检查,并且不遵循“此对象已被移动,现在它处于无效状态”的想法。
直播wandbox示例
#1
0
Your use of new
is a red flag, especially on a std::vector
.
你对new的使用是一个红旗,特别是在std :: vector上。
std::vector
s support move semantics natively. They are a memory management class. Manual memory management of a memory management class is a BIG red flag.
std :: vectors支持本机移动语义。它们是一个内存管理类。内存管理类的手动内存管理是一个很大的红旗。
Follow the rule of 0. =default
your move constructor, move assignment, copy constructor, destructor and copy assignment. Remove the *
from the vector. Don't allocate it. Replace data->
with data.
遵循规则0. =默认移动构造函数,移动赋值,复制构造函数,析构函数和复制赋值。从向量中删除*。不要分配它。用数据替换data->。
The second thing you should do is change:
你应该做的第二件事是改变:
MTensor::Tensor1D scalarProduct1D(MTensor::Tensor1D t1, double scalar) {
As it stands you take the first argument by value. That is great.
就目前而言,您可以按值获取第一个参数。那样太好了。
But once you take it by value, you should reuse it! Return t1
instead of creating a new temporary and returning it.
但是一旦你按价值看待它,你应该重复使用它!返回t1而不是创建一个新临时并返回它。
For that to be efficient, you will want to have a way to modify a tensor in-place.
为了提高效率,您需要有一种方法来就地修改张量。
void set(int i, double v) {
if(i>data->size()) {
std::cout << "index must be within vector dimension" << std::endl;
exit(1);
}
data.at(i) = v;
}
which gives us:
这给了我们:
MTensor::Tensor1D scalarProduct1D(MTensor::Tensor1D t1, double scalar) {
for(int i=0;i<t1.size();++i) {
ts.set(i, t1(i) * scalar);
}
return t1; // implicitly moved
}
We are now getting close.
我们现在越来越近了。
The final thing you have to do is this:
你要做的最后一件事是:
MTensor::Tensor1D t3(scalarProduct1D(std::move(t1),2));
to move the t1
into the scalarProduct1D
.
将t1移动到scalarProduct1D中。
A final problem with your code is that you use at
and you check bounds. at
's purpose is to check bounds. If you use at
, don't check bounds (do so with a try/catch). If you check bounds, use []
.
您的代码的最后一个问题是您使用at并检查边界。的目的是检查界限。如果使用at,请不要检查边界(使用try / catch执行此操作)。如果检查边界,请使用[]。
End result:
typedef std::vector<double> Tensor1DType;
class Tensor1D {
private:
//std::shared_ptr<Tensor1DType> data = std::make_shared<Tensor1DType>();
Tensor1DType data;
public:
Tensor1D() {};
Tensor1D(const Tensor1D& other)=default;
Tensor1D(Tensor1D&& other)=default;
~Tensor1D()=default;
Tensor1D& operator=(Tensor1D&& other)=default;
Tensor1D& operator=(Tensor1D const& other)=default;
Tensor1D(const std::initializer_list<double>& valuesList) {
insert(valuesList);
}
int size() const {
return data.size();
};
void insert(double value) {
data.push_back(value);
}
void insert(const std::initializer_list<double>& valuesList) {
data.insert( data.end(), valuesList.begin(), valuesList.end() );
}
double operator() (int i) const {
if(i>data.size()) {
std::cout << "index must be within vector dimension" << std::endl;
exit(1);
}
return data[i];
}
void set(int i, double v) {
if(i>data->size()) {
std::cout << "index must be within vector dimension" << std::endl;
exit(1);
}
data.at(i) = v;
}
static void printTensor(Tensor1DType const& info) {
for(double e : info) {
std::cout << e << "," << std::endl;
}
}
void printTensor() const {
printTensor(data);
}
};
MTensor::Tensor1D scalarProduct1D(MTensor::Tensor1D t1, double scalar) {
for(int i=0;i<t1.size();++i) {
t1.set(i, t1(i) * scalar);
}
return t1;
}
int main() {
MTensor::Tensor1D t1 = {1,2,3};
std::cout << "t1:" << std::endl;
t1.printTensor();
MTensor::Tensor1D t3(scalarProduct1D(std::move(t1),2));
std::cout << "t3:" << std::endl;
t3.printTensor();
std::cout << "t1:" << std::endl;
t1.printTensor();
return 0;
}
with a few other minor fixes (like using range-for, DRY, etc).
还有一些其他小修正(比如使用range-for,DRY等)。
#2
0
You need to move t1
when calling scalarProduct1D
, otherwise you'll make a copy:
调用scalarProduct1D时需要移动t1,否则你将复制:
MTensor::Tensor1D t3(scalarProduct1D(std::move(t1),2));
You need to explicitly use std::move
because t1
is an lvalue expression.
您需要显式使用std :: move,因为t1是左值表达式。
Note that you'll have to fix your printing functions to avoid dereferencing nullptr
if you want accessing the moved-from object to be a valid operation. I instead suggest to avoid making method invocation on moved-from objects valid as it requires additional checks and doesn't follow the idea of "this object has been moved, now it's in an invalid state".
请注意,如果要将移动的对象作为有效操作,则必须修复打印函数以避免取消引用nullptr。我反而建议避免对移动对象进行方法调用,因为它需要额外的检查,并且不遵循“此对象已被移动,现在它处于无效状态”的想法。
直播wandbox示例