3

我目前正在尝试使用Howard Hinnant 的 unique_ptr implementation,并且遇到了编译错误。这是一些示例代码:

struct Base {};

struct Derived : public Base {};

void testfun(boost::unique_ptr<Base>);

void test()
{
    unique_ptr<Derived> testDerived; 
    unique_ptr<Base> testBase(move(testDerived)); // ok, construct base explicitly from derived 
    testfun(move(testBase));                      // ok, pass base to testfun which expects base 
    testfun(unique_ptr<Base>(move(testDerived))); // ok, explicitly converts to unique_ptr<Base>
    testfun(move(testDerived));                   // error on this line
}

我得到的错误是

In function 'void test()':
error: no matching function for call to 'boost::unique_ptr<Base, boost::default_delete<Base> >::unique_ptr(boost::unique_ptr<Base, boost::default_delete<Base> >)'
note: candidates are: boost::unique_ptr<T, D>::unique_ptr(boost::detail_unique_ptr::rv<boost::unique_ptr<T, D> >) [with T = Base, D = boost::default_delete<Base>]
note:                 boost::unique_ptr<T, D>::unique_ptr(boost::unique_ptr<T, D>&) [with T = Base, D = boost::default_delete<Base>]
error:   initializing argument 1 of 'void testfun(boost::unique_ptr<Base, boost::default_delete<Base> >)' from result of 'boost::unique_ptr<T, D>::unique_ptr(boost::unique_ptr<U, E>, typename boost::enable_if_c<((((! boost::is_array<U>::value) && boost::detail_unique_ptr::is_convertible<typename boost::unique_ptr<U, boost::default_delete<U> >::pointer,typename boost::detail_unique_ptr::pointer_type<T, D>::type>::value) && boost::detail_unique_ptr::is_convertible<E,D>::value) && ((! boost::is_reference<D>::value) || boost::is_same<D,E>::value)), void>::type*) [with U = Derived, E = boost::default_delete<Derived>, T = Base, D = boost::default_delete<Base>]'

看起来有问题的线路不应该失败。这是实现中的错误,由于缺乏 C++0x 语言特性导致的实现限制,还是对 unique_ptrs 规则的误解?

(注意,我知道这在运行时不起作用,因为我不止一次移动相同的东西;我只是想找出编译时错误。)

4

2 回答 2

1

对于一个类似的例子,看到这个也应该失败

unique_ptr<Base> testBase = move(testDerived);

这里的问题是如何实现移动语义:“复制构造函数”采用非常量引用,因此无法绑定到临时对象。为了仍然从临时对象中“移动”,该类具有转换功能(以下实际上只是概念性的-它们可能在细节上有所不同):

operator rv<T>() { return rv<T>(*this); }

构造函数将获取该对象:

unique_ptr(rv<T> r):ptr_(r.release()) { }

这是一个由于相同原因而失败的示例:

// move helper. rv<> in unique_ptr
struct E { };

// simulates a unique_ptr<D>
struct D { };

// simulates the unique_ptr<B>
struct A {
  A() { }

  // accepts "derived" classes. Note that for unique_ptr, this will need that
  // the argument needs to be copied (we have a by-value parameter). Thus we 
  // automatically ensure only rvalue derived-class pointers are accepted.
  A(D) { } 

  // these will accept rvalues
  A(E) { }
  operator E() { return E(); }

private:
  A(A&); // private, error if passed lvalue
};

现在,考虑这段代码:

// allowed: goes: D -> A(D)
A a((D()));

// compile failure. Goes:
// D -> A(D) -> A(E)
A a = D();

复制初始化将首先转换为A. 但随后,临时A对象会尝试再次复制到最终对象。这将需要使用operator E. 但这是初始化中另一个用户定义的转换,标准禁止:

13.3.3.1/4

当在类复制初始化的第二步中调用临时复制时,[...],仅允许标准转换序列和省略号转换序列。

这就是您的代码失败的原因。

于 2010-03-02T19:28:42.640 回答
0

进一步的研究让我看到了这个注释,让我相信这是实现的一个已知限制:

目前有 3 个测试对我来说失败了(编译时失败,应该编译、运行和通过)。这些都与 [unique.ptr.single.ctor] 中指定的转换构造函数相关联。当源和目标的类型不同时,此模拟要求转换是显式的,并拒绝在隐式转换上编译:

unique_ptr<base> b(unique_ptr<derived>()); // ok

unique_ptr<base> b = unique_ptr<derived>(); // causes 3 compile time failures under unique.ptr/unique.ptr.single/unique.ptr.single.ctor .
于 2010-03-02T19:30:02.340 回答