【发布时间】:2017-05-28 06:59:49
【问题描述】:
我有一个 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 知道需要哪个调用,但我真的不确定这是最好的方法,并且也会感谢 cmets:
使用以下两个来代替之前的强制转换运算符:
operator T*() & {
return m_ptr;
}
operator T*() &&{
//Do something
return m_ptr;
}
现在operator T*() && 在使用 std::move 调用时调用,operator T*() & 在不使用它调用时调用。
【问题讨论】:
-
你的意思是在调用点区分还是被调用者区分?
-
被调用者是指
my_impl类型? -
用户期望不合理。如果您在某事上调用
std::move并将其传递给不拥有移动对象的函数,那么期望该对象为空或更改是不合理的。例如:std::shared_ptr<X> foo; ... std::move(foo)->bar();这不应该改变foo因为bar不占有。与bar(std::move(foo));相同。std::move只是给被调用函数permission 来移动对象,如果没有指定,它不会强制它移动它。 -
就个人而言,我不喜欢它。
unique_ptr没有做什么?从语义上讲,它应该表现得像一个指针,对吧?你为什么期望std::move(pointer)做一些特别的事情? -
不相关,确认
my_wrapper遵循五规则,并且my_wrapper(T*)构造函数是显式的。
标签: c++ c++11 templates operator-overloading move-semantics