我有一个C ++框架,我提供给我的用户,他们应该使用我自己的实现作为模板化类型编写的模板化包装器。 包装器充当RAII类,它包含指向用户类的实现的指针。 为了使用户的代码干净整洁(在我看来),我提供了一个转换操作符,它将我的包装器转换为它所持有的指针。这种方式(以及一些其他重载)用户可以使用我的包装器,就好像它是一个指针(很像shared_ptr)。
我遇到了一个角落案例,用户在我的包装器上使用std :: move调用一个函数,该函数获取指向其实现类的指针。以下是它的外观示例:
#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;
}
[这只是一个案例的例子,包装器中有更多的方法,但我很确定在这种情况下不起作用]
用户和我一样,如果在包装器上调用std :: move,那么在调用foo
之后,包装器将为空(或者至少被修改为好像被移动),但实际上,在foo
之前调用的唯一方法是强制转换操作符。
有没有办法让foo
之间的呼叫在foo
的两次呼叫之间有区别,即在使用和不使用std::move
时进行呼叫?
修改
感谢Mooing Duck的评论,我找到了my_wrapper
知道需要哪个电话的方式,但我真的不确定这是最好的方法,也会对此表示赞赏:
而不是之前的强制转换操作符使用以下两个:
operator T*() & {
return m_ptr;
}
operator T*() &&{
//Do something
return m_ptr;
}
现在使用std :: move调用时调用operator T*() &&
,并且在没有调用时调用operator T*() &
。
答案 0 :(得分:3)
用户和我一样,如果在包装器上调用了std :: move,那么在调用foo之后,包装器将为空(或者至少被修改为就像它被移动一样)
你的期望是错误的。只有在移动发生时,即如果转移了某种资源的所有权,它才会被修改。但是调用foo
并没有做那样的事情,因为它只是访问包装器中保存的指针。调用std::move
除了将其参数转换为rvalue之外不会做任何事情,而rvalue不会改变它。某些通过引用接受rvalue的函数可能会修改它,因此std::move
启用它,但它本身不会这样做。如果没有将右值传递给这样的函数,则不会进行任何修改。
如果确实希望将其设为空,则可以添加重载来执行此操作:
template<typename T>
void foo(my_wrapper<T>&& w) {
foo(static_cast<my_interface*>(w));
w = my_wrapper<T>{}; // leave it empty
}
但是......为什么?为什么要这样做?
如果您执行以下操作,则包装器不会为空:
my_wrapper<my_impl> w(new my_impl);
my_wrapper<my_impl> w2 = std::move(w);
并没有留空:
my_wrapper<my_impl> w(new my_impl);
my_wrapper<my_impl> w2;
w2 = std::move(w);
如果复制rvalue包装器不会将其留空,为什么只需访问其成员就将其留空?这毫无意义。
即使你的包装器有一个移动构造函数和移动赋值运算符,以便上面的例子使w
为空,这仍然不意味着访问一个右值对象的成员应该修改对象。为什么operator T*
转换是对左值还是右值进行转换会产生任何逻辑差异?
(另外,你真的确定从包装指针类型隐式转换到和是一个好主意吗?提示:这不是一个好主意。通常更喜欢让你的转换显式,特别是,如果你正在处理动态分配对象的指针。)