C ++ std ::移动指针 [英] C++ std::move a pointer

查看:343
本文介绍了C ++ std ::移动指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个C ++框架,可以提供给我的用户,这些用户应该使用我编写的模板包装器以及自己的实现作为模板类型。
包装器充当RAII类,并且包含指向用户类的实现的指针。
为了使用户的代码整洁(我认为),我提供了一个强制转换运算符,它将包装器转换为其持有的指针。这样(连同其他一些重载),用户可以使用我的包装器,就像它是一个指针一样(就像shared_ptr一样。)。

I have a C++ framework which I provide to my users, who should use a templated wrapper I wrote with their own implementation as the templated type. The wrapper acts as an RAII class and it holds a pointer to an implementation of the user's class. To make the user's code clean and neat (in my opinion) I provide a cast operator which converts my wrapper to the pointer it holds. This way (along with some other overloads) the user can use my wrapper as if it is a pointer (much like a shared_ptr).

我遇到了一个极端情况用户使用包装器上的std :: move调用一个函数,该函数使用指向其实现类的指针。这是一个看起来像的例子:

I came across a corner case where a user calls a function, which takes a pointer to his implementation class, using std::move on my wrapper. Here's an example of what it looks like:

#include <iostream>
using namespace std;

struct my_interface {
    virtual int bar() = 0;
};

template <typename T>
struct my_base : public my_interface {
    int bar() { return 4; }
};

struct my_impl : public my_base<int> {};

template <typename T>
struct my_wrapper {
    my_wrapper(T* t) {
        m_ptr = t;
    }

    operator T*() {
        return m_ptr;
    }

private:
    T* m_ptr;
};

void foo(my_interface* a) {
    std::cout << a->bar() << std::endl;
}


int main()
{
    my_impl* impl = new my_impl();
    my_wrapper<my_impl> wrapper(impl);
    foo(std::move(wrapper));
    //foo(wrapper);

    return 0;
}

[当然,这只是一个例子,还有更多方法在包装程序中,但我很确定在这种情况下不会在这里起作用]

[This is ofcourse just an example of the case, and there are more methods in the wrapper, but I'm pretty sure that don't play a role here in this case]

用户,就像我希望的那样,如果std ::在包装器上调用了move,然后在调用 foo 之后,包装器将为空(或至少已被修改,就像被移动一样),但实际上唯一的方法是在 foo 之前调用的是强制转换运算符。

The user, as would I, expect that if std::move was called on the wrapper, then after the call to foo the wrapper will be empty (or at least modified as if it was moved), but in reality the only method being invoked before foo is the cast operator.

有没有办法调用 foo 可以在对 foo 的两次调用之间进行区分,即在使用和不使用 std :: move

Is there a way to make the call to foo distinguishable between the two calls to foo i.e when calling with and without std::move?

编辑
多亏了Mooing Duck的评论,我找到了一种方式来 my_wrapper 知道需要哪个呼叫,但是我真的不确定这是最好的方法,也将对此表示赞赏:

EDIT Thanks to the Mooing Duck's comment I found a way that my_wrapper knows which call is required, but I'm really not sure this is the best method to go with and will appreciate comments on this as well:

代替以前的演员,使用以下两次:

Instead of the previous cast operator use the following two:

operator T*() & {
    return m_ptr;
}

operator T*() &&{
    //Do something
    return m_ptr;
}

现在操作员T *()&& :move调用时调用,而没有std :: move调用时调用运算符T *()& 。 $ b

now operator T*() && is called when calling with std::move and operator T*() & is called when calling without it.

推荐答案


用户,就像我希望的那样,如果在包装器上调用了std :: move,则在调用之后将foo设为foo,则包装器将为空(或至少经过修改,就好像它被移动了一样)

The user, as would I, expect that if std::move was called on the wrapper, then after the call to foo the wrapper will be empty (or at least modified as if it was moved)

您的期望是错误的。仅当移动发生时,即转移某种资源的所有权时,才会对其进行修改。但是调用 foo 不会做任何事情,因为它只能访问包装器中保存的指针。调用 std :: move 不会执行任何操作,只是将其参数强制转换为右值,这不会改变它。某些通过引用接受右值的函数可能会对其进行修改,因此 std :: move 会启用该值,但它本身不会这样做。如果您不将右值传递给此类函数,则不会进行任何修改。

Your expectation is wrong. It will only be modified if a move happens, i.e. if ownership of some kind of resource is transferred. But calling foo doesn't do anything like that, because it just gets access to the pointer held inside the wrapper. Calling std::move doesn't do anything except cast its argument to an rvalue, which doesn't alter it. Some function which accepts an rvalue by reference might modify it, so std::move enables that, but it doesn't do that itself. If you don't pass the rvalue to such a function then no modification takes place.

如果您真的希望将其设置为空,则可以添加重载来做到这一点:

If you really want to make it empty you can add an overload to do that:

template<typename T>
void foo(my_wrapper<T>&& w) {
    foo(static_cast<my_interface*>(w));
    w = my_wrapper<T>{};  // leave it empty
}

但是...为什么?为什么要这样做?

But ... why? Why should it do that?

如果您这样做,包装器不会留空:

The wrapper isn't left empty if you do:

my_wrapper<my_impl> w(new my_impl);
my_wrapper<my_impl> w2 = std::move(w);

并且不会被以下人留空:

And isn't left empty by:

my_wrapper<my_impl> w(new my_impl);
my_wrapper<my_impl> w2;
w2 = std::move(w);

如果复制右值包装器不能将其保留为空,为什么应该简单地访问其成员将其留为空白?

If copying an rvalue wrapper doesn't leave it empty, why should simply accessing its member leave it empty? That makes no sense.

即使您的包装器具有move构造函数和move赋值运算符,因此 do 上面的示例仍保留 w 为空,这仍然并不意味着访问右值对象的成员应该修改该对象。为什么运算符T * 转换为左值还是右值有什么逻辑上的区别?

Even if your wrapper has a move constructor and move assignment operator so that the examples above do leave w empty, that still doesn't mean that accessing the member of an rvalue object should modify the object. Why does it make any logical difference whether the operator T* conversion is done to an lvalue or an rvalue?

(此外,您是否真的确定从包装的指针类型进行到的隐式转换都是一个好主意?提示:这不是一个好主意。通常,您最好将转换显式地显示为尤其是,如果您正在处理指向动态分配对象的指针。)

(Also, are you really sure that having implicit conversions both to and from the wrapped pointer type is a good idea? Hint: it's not a good idea. In general prefer to make your conversions explicit, especially if you're dealing with pointers to dynamically-allocated objects.)

这篇关于C ++ std ::移动指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆