C++11,返回向量函数风格的移动语义
C++11, move semantics of returning vectors functional style
我正在创建一个示例来尝试理解使用 c++11 时如何优化移动语义。
首先,我有以下两个函数,它们接受一些输入和 return 输入的修改版本。
std::vector<float> abs(const std::vector<float> &v)
{
std::vector<float> abs_vec = v;
for (auto &val: abs_vec) {
auto i = &val - &abs_vec[0];
abs_vec[i] = std::abs(abs_vec[i]);
}
return abs_vec;
}
std::vector<float> normalize(const std::vector<float> &v)
{
std::vector<float> norm_vec = v;
auto max_val = std::max_element(std::begin(norm_vec), std::end(norm_vec));
auto min_val = std::min_element(std::begin(norm_vec), std::end(norm_vec));
auto norm_factor = *max_val;
auto min_check = std::abs(*min_val);
if (min_check > std::abs(norm_factor)) {
norm_factor = min_check;
}
for (auto &val: norm_vec) {
auto i = &val - &norm_vec[0];
norm_vec[i] = norm_vec[i] / norm_factor;
}
return norm_vec;
}
然后我用下面的代码测试函数:
int main()
{
std::vector<float> tmp{-1,2,-3,4,5};
printf("pointer before: %p\n", &tmp[0]);
tmp = normalize(tmp);
printf("pointer after normalize: %p\n", &tmp[0]);
tmp = abs(tmp);
printf("pointer after abs: %p\n", &tmp[0]);
};
由此我得到输出:
pointer before: 0x156dc20
pointer after normalize: 0x156e050
pointer after abs: 0x156dc20
谁能解释一下这是怎么回事,为什么我在规范化后似乎得到了一份副本,但在调用 abs 函数后却与原始内存位置相同?
有没有更好的方法来编写像这样的函数式风格的函数,从而产生更优化的代码?
我不太清楚为什么调用后第一个元素的内存地址是一样的abs()
,但是有更好的写法。如果使用 std::transform()
,代码会更简单,可在 algorithm
header:
std::vector<float> abs(const std::vector<float> &v)
{
std::vector<float> abs_vec;
std::transform(v.begin(), v.end(), std::back_inserter(abs_vec),
[] (float val) {
return std::abs(val);
});
return abs_vec;
}
这将遍历 v
中的所有元素,并为每个元素调用 lambda 函数。该函数的 return 值将被插入到 abs_vec
的末尾。
normalize()
末尾的循环也可以重写为使用 std::transform()
。在这种情况下,可以捕获 norm_factor
变量,以便可以在 lambda 中使用它:
std::transform(v.begin(), v.end(), std::back_inserter(norm_vec),
[norm_factor] (float val) {
return val / norm_factor;
});
你printf
的值是vector底层数据成员的地址。
所以我想,在你的情况下
1) 创建tmp
在位置0x156dc20
创建(分配)一个数据成员
2) normalize()
操作将数据成员替换为在normalize()
(norm_vec
)中创建的向量的数据成员,地址为0x156e050,并释放内存前一个数据成员
3) abs()
操作用abs()
(abs_vec
)中创建的vector
的数据成员替换数据成员,即再次0x156dc20,回收之前释放的内存
简要说明:我想 abs_vect
回收了 tmp
之前使用的内存
恩,你的 for-each 循环太复杂了。
循环
for (auto &val: norm_vec) {
auto i = &val - &norm_vec[0];
norm_vec[i] = norm_vec[i] / norm_factor;
}
可以简化为(你使用的是引用(&
))为
for (auto &val: norm_vec)
val /= norm_factor;
和循环
for (auto &val: abs_vec) {
auto i = &val - &abs_vec[0];
abs_vec[i] = std::abs(abs_vec[i]);
}
可以简化(同理)为
for (auto &val: abs_vec)
val = std::abs(val);
根据安迪的建议,std::transform
的使用(恕我直言)更好。
关于norm_factor
的发现,我认为
auto pairIt = std::minmax_element(norm_vec.begin(), norm_vec.end());
auto norm_factor = std::max(std::abs(*(pairIt.first)), *(pairIt.second));
比
更简单(我想也更优化)
auto max_val = std::max_element(std::begin(norm_vec), std::end(norm_vec));
auto min_val = std::min_element(std::begin(norm_vec), std::end(norm_vec));
auto norm_factor = *max_val;
auto min_check = std::abs(*min_val);
if (min_check > std::abs(norm_factor)) {
norm_factor = min_check;
}
p.s.: 对不起我的英语不好
显然,当您从不能移动的常量引用创建新副本时。如果您想以向量移入和移出它们的方式编写这些函数,则需要按值(此处应首选)或右值引用传递向量,并将向量移入函数。
#include <cstdio>
#include <vector>
std::vector<float> plus_five(std::vector<float> v)
{
for (auto& e : v)
e += 5;
return v;
}
int main()
{
std::vector<float> v{1, 2, 3, 4};
std::printf("%p\n", v.data());
v = plus_five(std::move(v)); // move
std::printf("%p\n", v.data());
v = plus_five(v); // copy
std::printf("%p\n", v.data());
}
我正在创建一个示例来尝试理解使用 c++11 时如何优化移动语义。
首先,我有以下两个函数,它们接受一些输入和 return 输入的修改版本。
std::vector<float> abs(const std::vector<float> &v)
{
std::vector<float> abs_vec = v;
for (auto &val: abs_vec) {
auto i = &val - &abs_vec[0];
abs_vec[i] = std::abs(abs_vec[i]);
}
return abs_vec;
}
std::vector<float> normalize(const std::vector<float> &v)
{
std::vector<float> norm_vec = v;
auto max_val = std::max_element(std::begin(norm_vec), std::end(norm_vec));
auto min_val = std::min_element(std::begin(norm_vec), std::end(norm_vec));
auto norm_factor = *max_val;
auto min_check = std::abs(*min_val);
if (min_check > std::abs(norm_factor)) {
norm_factor = min_check;
}
for (auto &val: norm_vec) {
auto i = &val - &norm_vec[0];
norm_vec[i] = norm_vec[i] / norm_factor;
}
return norm_vec;
}
然后我用下面的代码测试函数:
int main()
{
std::vector<float> tmp{-1,2,-3,4,5};
printf("pointer before: %p\n", &tmp[0]);
tmp = normalize(tmp);
printf("pointer after normalize: %p\n", &tmp[0]);
tmp = abs(tmp);
printf("pointer after abs: %p\n", &tmp[0]);
};
由此我得到输出:
pointer before: 0x156dc20
pointer after normalize: 0x156e050
pointer after abs: 0x156dc20
谁能解释一下这是怎么回事,为什么我在规范化后似乎得到了一份副本,但在调用 abs 函数后却与原始内存位置相同?
有没有更好的方法来编写像这样的函数式风格的函数,从而产生更优化的代码?
我不太清楚为什么调用后第一个元素的内存地址是一样的abs()
,但是有更好的写法。如果使用 std::transform()
,代码会更简单,可在 algorithm
header:
std::vector<float> abs(const std::vector<float> &v)
{
std::vector<float> abs_vec;
std::transform(v.begin(), v.end(), std::back_inserter(abs_vec),
[] (float val) {
return std::abs(val);
});
return abs_vec;
}
这将遍历 v
中的所有元素,并为每个元素调用 lambda 函数。该函数的 return 值将被插入到 abs_vec
的末尾。
normalize()
末尾的循环也可以重写为使用 std::transform()
。在这种情况下,可以捕获 norm_factor
变量,以便可以在 lambda 中使用它:
std::transform(v.begin(), v.end(), std::back_inserter(norm_vec),
[norm_factor] (float val) {
return val / norm_factor;
});
你printf
的值是vector底层数据成员的地址。
所以我想,在你的情况下
1) 创建tmp
在位置0x156dc20
2) normalize()
操作将数据成员替换为在normalize()
(norm_vec
)中创建的向量的数据成员,地址为0x156e050,并释放内存前一个数据成员
3) abs()
操作用abs()
(abs_vec
)中创建的vector
的数据成员替换数据成员,即再次0x156dc20,回收之前释放的内存
简要说明:我想 abs_vect
回收了 tmp
恩,你的 for-each 循环太复杂了。
循环
for (auto &val: norm_vec) {
auto i = &val - &norm_vec[0];
norm_vec[i] = norm_vec[i] / norm_factor;
}
可以简化为(你使用的是引用(&
))为
for (auto &val: norm_vec)
val /= norm_factor;
和循环
for (auto &val: abs_vec) {
auto i = &val - &abs_vec[0];
abs_vec[i] = std::abs(abs_vec[i]);
}
可以简化(同理)为
for (auto &val: abs_vec)
val = std::abs(val);
根据安迪的建议,std::transform
的使用(恕我直言)更好。
关于norm_factor
的发现,我认为
auto pairIt = std::minmax_element(norm_vec.begin(), norm_vec.end());
auto norm_factor = std::max(std::abs(*(pairIt.first)), *(pairIt.second));
比
更简单(我想也更优化)auto max_val = std::max_element(std::begin(norm_vec), std::end(norm_vec));
auto min_val = std::min_element(std::begin(norm_vec), std::end(norm_vec));
auto norm_factor = *max_val;
auto min_check = std::abs(*min_val);
if (min_check > std::abs(norm_factor)) {
norm_factor = min_check;
}
p.s.: 对不起我的英语不好
显然,当您从不能移动的常量引用创建新副本时。如果您想以向量移入和移出它们的方式编写这些函数,则需要按值(此处应首选)或右值引用传递向量,并将向量移入函数。
#include <cstdio>
#include <vector>
std::vector<float> plus_five(std::vector<float> v)
{
for (auto& e : v)
e += 5;
return v;
}
int main()
{
std::vector<float> v{1, 2, 3, 4};
std::printf("%p\n", v.data());
v = plus_five(std::move(v)); // move
std::printf("%p\n", v.data());
v = plus_five(v); // copy
std::printf("%p\n", v.data());
}