只对右值参考的一些资源进行食人
Cannibalizing just some of the resources of an rvalue ref
我的意图如下:接收一个右值引用(即对我想拆散的对象的引用),移除它的一些资源,然后返回其他资源。我写了这个代码:
std::vector<int> dosomething(std::vector<int> && vec) {
// do something with vec (e.g. consume SOME of its resources)
return std::move(vec);
}
但我不确定是否:
- 这是实现我想要的正确方式吗
- 我应该使用
std::forward
吗?我不相信,因为这不是一个普遍的参考
这是实现我想要的正确方式吗?
好吧,让我们忽略这样一个事实,即vector<int>
不是一个只带一些资源潜逃是合理的类型(它只有一个资源:分配)。我会假装你说的是某种实际上拥有多种资源的类型。
我认为这样的函数是对本身的歪曲。
一个函数声明它将一个类型作为右值引用,这表明它将如何处理该值。这意味着它将直接或间接地将该对象移动到另一个该类型的对象中。它可以自己做,也可以打电话给其他愿意做的人。但在函数返回之前,会发生这种情况。
我们的想法是让这个代码的含义与它所说的完全一样:
SomeFunction(std::move(value));
看起来value
正在被移动到SomeFunction
中,就像您执行SomeType t = std::move(value);
一样。在这两种情况下,value
的状态都应该从移动。否则会使代码变得混乱。
一个函数想要任意修改一个参数,使其处于某种新状态,应该将该参数作为一个非常值左值引用,而不是右值引用。"modify"的确切含义取决于函数及其需要做的事情。如果你想用一些资源而不是其他资源潜逃,你可以用非常值引用来实现。您甚至可以从非常值引用的子对象中std::move
。但是,您和您的用户应该了解函数调用后对象的状态。
当您通过右值引用获取参数时,这是"当我返回时,此对象将处于移出状态"的简写。请不要将右值引用误传为非常量左值引用。
我的意图如下:接收一个右值引用(即对我想拆散的对象的引用),删除它的一些资源,然后返回其他资源。
这没关系,但两者都可能更好:
-
完全删除资源占位符,或
-
用其他对象或替换删除的资源
-
向呼叫者指示删除了哪些资源
因为如果你不这样做,那么用户可能会使用你拆散的其中一个移动项目。由于从对象(至少在STL中)移动的对象具有"有效但未定义"状态,因此使用这些拆包的对象将产生未定义的结果。
你不想那样。
这是实现我想要的正确方式吗?
看起来不错
我应该使用std::forward吗?我相信不会,因为这不是一个通用的参考
不,你不应该。你的推理是正确的。
仅供参考:"向用户指示删除了哪些资源"在标准库中有一个模型。看看std::remove_if
——它实际上将"已删除"的项移动到序列的末尾,并返回一个迭代器,标记剩余项的end
和"已删除的"项的开头。
这是我处理这个问题的一种方法。
目标:对向量中所有在词汇上大于"ccc"的字符串执行操作,使向量中的其余字符串按原始顺序
template<class T>
void do_something_with(T t)
{
std::cout << "did something with " << t << std::endl;
}
template<class ForwardIt, class UnaryPredicate>
ForwardIt rotate_if(ForwardIt first, ForwardIt last, UnaryPredicate p)
{
while(first != last)
{
auto n = std::next(first);
if (p(*first))
{
if (n != last) {
std::rotate(first, n, last);
first = n;
last = std::prev(last);
}
else {
last = first;
break;
}
}
first = n;
}
return last;
}
template<class T, class Pred, class Model>
std::vector<T> do_something_if(std::vector<T>&& in, Pred pred, Model&& model)
{
auto result = std::move(in);
auto ibreak = rotate_if(std::begin(result),
std::end(result),
[&](auto& v) {
auto match = pred(v, model);
return match;
});
for (auto i = ibreak ; i != std::end(result) ; ++i) {
do_something_with(std::move(*i));
}
result.erase(ibreak, std::end(result));
return result;
}
void test()
{
std::vector<std::string> words = {
"yyy",
"aaa",
"zzz",
"bbb",
};
auto filtered = do_something_if(std::move(words), std::greater<>(), "ccc");
std::cout << "remaining items:n";
for (auto& w : filtered) {
std::cout << w << std::endl;
}
}
预期输出:
did something with zzz
did something with yyy
remaining items:
aaa
bbb
在大多数情况下都会有更有效的解决方案,但这一解决方案避免了内存分配。
我假设std::vector<int>
参数是一些更复杂类型的占位符,例如一些std::vector<Resource>
。你不能移动int
,你唯一能获得的其他资源就是向量本身的数据。
- 这是实现我想要的正确方式吗
是的。如果省略std::move
,则vec
将被视为左值,并将其复制到返回中。
您可以通过以下示例观察这种行为:
struct C
{
C() {}
C( C const& ) { std::cout << "copyn"; }
C( C&& ) { std::cout << "moven"; }
};
C f( C&& c )
{
return std::move( c ); // cast to rvalue reference, invokes the move ctor
}
C g( C&& c )
{
return c; // does not cast to rvalue reference, will invoke the copy ctor
}
int main()
{
f( C{} ); // calling with rvalue, the return causes the move constructor to be called.
g( C{} ); // calling with rvalue, but no std::move means calling the copy constructor.
}
- 我应该使用std::forward吗?我不相信,因为这不是一个普遍的参考
没有。这不会有什么坏处,因为std::forward<T>
有条件地强制转换(左值不会被转换为右值引用,右值会被转换)。然而,如果有更多的类型,它将基本上实现与std::move
相同的功能。
根据经验,std::move
与右值一起使用,std::forward<T>
与通用引用一起使用。
- 具有瞬态资源的RAII类
- C++ 雷神库 - 使用资源加载器类时出现问题(不命名类型)
- 允许从 std::map 的密钥窃取资源?
- 食人魔异常(6:文件未找到异常):找不到'ogre.cfg'文件
- 食人魔相机方向给定矢量和角度
- 食人魔:模拟透视视图缩放对象
- 食人魔3d /合成器相关问题
- 只对右值参考的一些资源进行食人
- 三个.js的食人魔出口商
- 食人魔投影仪透过物体发光
- OpenCV相机内在矩阵到用于增强现实的食人魔投影矩阵
- 为安卓 ndk10 构建食人魔
- 食人魔点场景保存
- 使用TDD进行食人魔游戏编程
- C++食人魔,无法编译和运行
- 食人魔错误LNK2001:无法解析的外部符号
- 在食人魔中访问视口像素值
- C++可能的线程问题 - 可能是食人魔的错
- 麻烦编译食人魔教程示例
- 编译食人魔代码时出错