【问题标题】:Remove ambiguity in function call on implicitly converted smart pointer在隐式转换的智能指针上消除函数调用中的歧义
【发布时间】:2017-04-16 06:45:14
【问题描述】:

我正在阅读 Scott Meyers 的 More Effective C++ 的智能指针的第 28 项,并且有以下问题。

完整的演示可以在http://ideone.com/aKq6C0 找到。

派生类指针可以隐式转换为基类指针:

class Base {};
class Derived : public Base {};
void foo(Base* b) { cout << "foo called on Base pointer" << endl;}
Derived *d = new Derived();
foo(d); //No problem

但是这种隐式转换不能发生在智能指针上,即SmartPtr&lt;Derived&gt;不能隐式转换为SmartPtr&lt;Base&gt;。所以我们使用成员模板进行此类转换:

template<typename T>
class SmartPtr {
public:
  //constructors, operator->, etc

  //member template for type conversion
  template<NewType>
  operator SmartPtr<NewType> () {
    return SmartPtr<NewType>(pointee);
  }
private:
  T* pointee;//the raw pointer
};

这几乎可以工作,但它可能会导致歧义:

class Remote {};
class Base : public Remote {};
class Derived : public Base {};
void foo(const SmartPtr<Remote>& p) { cout << "remote" << endl;}
void foo(const SmartPtr<Base>& p) { cout << "base" << endl;}

SmartPtr<Derived> d(new Derived());
foo(d);//compile error: ambiguity

在此示例中,编译器不知道是否应将d 转换为SmartPtr&lt;Base&gt;SmartPtr&lt;Remote&gt;,尽管对于原始指针Base 显然更胜一筹。书上说

我们能做的最好的事情是使用成员模板来生成转换函数,然后在产生歧义的情况下使用强制转换。

但是我们究竟如何在这里应用演员表? foo(static_cast&lt;SmartPtr&lt;Base&gt;&gt;(d)) 也不编译。从错误消息中我可以看出错误来自SmartPtr 的复制构造函数中使用非常量引用。我想知道进行函数调用的正确方法是什么。

【问题讨论】:

  • 不回答问题,但return SmartPtr&lt;NewType&gt;(pointee); 似乎不对,你需要std::week_ptr 之类的东西才能做到这一点
  • 您应该查看标准智能指针,以及当(且仅当)Derived* 可转换为 Base* 时,它们如何提供从 std::shared_ptr&lt;Derived&gt;std::shared_ptr&lt;Base&gt; 的转换(例如)。
  • 您的演员表是正确的,缺少的是您的复制构造函数的代码。如果您有一个采用非常量 ref - 您会收到您提到的错误,如果您可以在复制 ctor 中将 ref 更改为 const 并且代码将编译(测试)

标签: c++ smart-pointers implicit-conversion ambiguous


【解决方案1】:
//constructors

这是你省略的最重要的部分:)

您的演员表是正确的,这完全取决于您拥有的一组构造函数。如果你有一个接受非常量 ref 的函数 - 你会得到你提到的错误,如果你可以在复制 ctor 中将 ref 更改为 const 并且代码会编译。

可能不是很明显,但是当你调用时

return SmartPtr<NewType>(pointee);

您正在构建必须接受T* 的新SmartPtr&lt;NewType&gt;,而NewTypeT 是不同的类型。很可能您只有接受相同类型的原始指针的ctor(即T* 用于SmartPtr&lt;T&gt;X* 用于SmartPtr&lt;X&gt;)因此编译器正在寻找另一个converting ctor 来创建您的新SmartPtr 并找到您的复制ctor ,但它不能将新值绑定到非 const ref


编辑:

如果您使用的是 c++11,添加 move ctor 也可以解决您的问题,因为它可以绑定到右值

SmartPtr( SmartPtr<T>&& other): pointee(other.pointee) { other.pointee = nullptr; }

【讨论】:

  • 感谢奥列格的帮助。您可以在我在上面提供的 ideone 页面中查看 SmartPtr 的完整定义 ideone.com/aKq6C0 。我直接从书中得到它,SmartPtr 的行为基本上决定了它必须在其复制构造函数中采用非常量引用(有点像已弃用的 auto_ptr)。
  • 你在使用 c++11 吗?您也可以通过添加移动 ctor 来解决您的问题? SmartPtr( SmartPtr&lt;T&gt;&amp;&amp;) ..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-25
  • 2019-06-18
  • 1970-01-01
  • 1970-01-01
  • 2015-02-03
  • 1970-01-01
相关资源
最近更新 更多