只能对rvalue ref的一些资源进行自我修复

时间:2016-05-12 17:01:52

标签: c++

我的意图如下:接收右值引用(即对我想要蚕食的对象的引用),删除某些的资源,然后返回其他资源。我写了这段代码:

std::vector<int> dosomething(std::vector<int> && vec) {
    // do something with vec (e.g. consume SOME of its resources)
    return std::move(vec);
}

但我不确定是否:

  1. 这是实现我想要的正确方法吗?
  2. 我应该使用std::forward吗?我相信不是因为这不是普遍的参考

3 个答案:

答案 0 :(得分:5)

  

这是实现我想要的正确方法吗?

好吧,让我们忽略这样一个事实:vector<int>不是一种类型,只有一些资源才会潜逃(它只有一个资源:分配)。我假装你正在谈论实际上有多种资源的某种类型。

我认为这样的功能是歪曲本身。

一个声明它将一个类型作为右值引用的函数正在说非常重要关于它将对该值做什么。它说要直接或间接地将该对象移动到该类型的另一个对象中。它可以自己做,也可以打电话给别人。但这就是函数返回之前会发生的事情。

这个想法是让这段代码完全符合它的含义:

SomeFunction(std::move(value));

看起来value被移入SomeFunction,就像你SomeType t = std::move(value);一样。在这两种情况下,都应该移动value的状态。否则会使代码混乱。

希望任意修改参数,使其处于某种新状态的函数应该将参数作为非const 左值引用,而不是右值引用。究竟是什么&#34;修改&#34;手段取决于功能以及它需要做什么。如果你想使用某些资源而不是其他资源潜逃,你可以使用非const左值引用来做到这一点。您甚至可以从非const左值引用的子对象std::move。但是您和您的用户应该了解函数调用后对象的状态。

当您通过右值参考获取参数时,这是&#34;的简写,当我返回时,此对象将处于移动状态。&#34;请不要将rvalue引用误传为非限定左值引用。

答案 1 :(得分:3)

  

我的意图如下:接收一个右值引用(即对我想要蚕食的对象的引用),删除它的一些资源,然后返回其他资源。

没关系,但它可能更好:

  • 完全删除资源占位符,或

  • 将删除的资源替换为其他对象,或

  • 向来电者表明删除了哪些资源

因为如果您不执行其中一项操作,那么用户可以使用您所移动的其中一个项目。由于移动的对象(至少在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

在大多数情况下会有更有效的解决方案,但这个避免了内存分配。

答案 2 :(得分:2)

我假设std::vector<int>参数是某个更复杂类型的占位符,例如某些std::vector<Resource>。您无法移动 int,而您唯一能够获取的其他资源就是矢量本身的数据。

  
      
  1. 这是实现我想要的正确方法吗?
  2.   

是。如果省略std::movevec将被视为左值,它将被复制到返回值。

您可以使用此示例观察此行为:

struct C
{
    C() {}
    C( C const& ) { std::cout << "copy\n"; }
    C( C&& ) { std::cout << "move\n"; }
};

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.
}
  
      
  1. 我应该使用std :: forward吗?我相信不是因为这不是普遍的参考
  2.   

没有。从std::forward<T>有条件地施放(lvalues不会被施放右值引用,rvalues将是),它不会受到伤害。但是,它基本上与std::move相同,但输入更多。

根据经验,您使用带有rvalues的std::move和带有通用引用的std::forward<T>