简体   繁体   English

unique_ptr什么时候需要完整的类型?

[英]When does unique_ptr require a complete type?

In the code below function f() can call the operator bool() and operator *() member functions of unique_ptr<C> for the incomplete class C . 在下面的代码中,函数f()可以为不完整的class C调用unique_ptr<C>operator bool()operator *()成员函数。 However when function g() tries to call those same member functions for unique_ptr<X<C>> , the compiler suddenly wants a complete type and tries to instantiate X<C> , which then fails. 但是当函数g()尝试为unique_ptr<X<C>>调用那些相同的成员函数时,编译器突然想要一个完整的类型并尝试实例化X<C> ,然后失败。 For some reason unique_ptr<X<C>>::get() does not cause template instantiation and compiles correctly as can be seen in function h() . 由于某种原因, unique_ptr<X<C>>::get()不会导致模板实例化并正确编译,如函数h() Why is that? 这是为什么? What makes get() different from operator bool() and operator *() ? 是什么让get()operator bool()operator *()

#include <memory>

class C;
std::unique_ptr<C> pC;

C& f() {
    if ( !pC ) throw 0; // OK, even though C is incomplete
    return *pC;         // OK, even though C is incomplete
}

template <class T>
class X
{
    T t;
};

std::unique_ptr<X<C>> pX;

X<C>& g() {
    if ( !pX ) throw 0; // Error: 'X<C>::t' uses undefined class 'C'
    return *pX;         // Error: 'X<C>::t' uses undefined class 'C'
}

X<C>& h() {
    if ( !pX.get() ) throw 0; // OK
    return *pX.get();         // OK
}

class C {};

Here's a contrived and simplified example, using only our own types: 这是一个人为的简化示例,仅使用我们自己的类型:

class Incomplete;

template <class T>
struct Wrap {
    T t;
};

template <class T>
struct Ptr {
    T* p;

    void foo() { }
};

template <class T>
void foo(Ptr<T> ) { }

int main() {
    Ptr<Incomplete>{}.foo();         // OK
    foo(Ptr<Incomplete>{});          // OK

    Ptr<Wrap<Incomplete>>{}.foo();   // OK
    ::foo(Ptr<Wrap<Incomplete>>{});  // OK!
    foo(Ptr<Wrap<Incomplete>>{});    // error
}

The problem is, when we make an unqualified call to foo , as opposed to a qualified call to ::foo or calling the member function Ptr<T>::foo() , we're triggering argument-dependent lookup. 问题是,当我们对foo进行非限定调用时,而不是对::foo进行限定调用或调用成员函数Ptr<T>::foo() ,我们将触发与参数相关的查找。

ADL will look in the associated namespaces of template types in class template specializations, which will trigger implicit template instantiation. ADL将在类模板特化中查找模板类型的关联命名空间,这将触发隐式模板实例化。 Template instantiation needs to be triggered in order to perform ADL lookup because, for instance, Wrap<Incomplete> could declare a friend void foo(Ptr<Wrap<Incomplete >> ) which would need to be invoked. 需要触发模板实例化以执行ADL查找,例如, Wrap<Incomplete>可以声明需要调用的friend void foo(Ptr<Wrap<Incomplete >> ) Or Wrap<Incomplete> might have dependent bases whose namespaces need to also be considered. Wrap<Incomplete>可能具有依赖基础,其名称空间也需要考虑。 Instantiation at this point makes the code ill-formed because Incomplete is an incomplete type and you can't have a member of an incomplete type. 此时的实例化会使代码格式错误,因为Incomplete是一个不完整的类型,并且您不能拥有不完整类型的成员。

Getting back to the original question, the calls to !pX and *pX invoke ADL which leads to the instantation of X<C> which is ill-formed. 回到最初的问题,对!pX*pX调用会调用ADL,这会导致X<C>的瞬间形成错误。 The call to pX.get() does not invoke ADL, which is why that one works fine. pX.get()调用不会调用ADL,这就是为什么一个正常工作。


See this answer for more details, also CWG 557 . 有关详细信息,请参阅此答案CWG 557

It's not the unique_ptr that requires the complete type, it's your class X that does. 这不是需要完整类型的unique_ptr ,而是你的类X

std::unique_ptr<C> pC;

You don't actually do any allocation yet for C so the compiler doesn't need to know the specifics of C here. 你实际上还没有为C做任何分配,所以编译器不需要知道C的具体细节。

std::unique_ptr<X<C>> pX;

Here, you use C as a template type for X . 在这里,您使用C作为X的模板类型。 Because X contains an object of type T which is C here the compiler needs to know what to allocate when X is instantiated. 因为X包含类型为T的对象,这里是C ,编译器需要知道在实例化X时要分配的内容。 ( t is an object and thus instantiated on construction). t是一个对象,因此在构造时实例化)。 Change T t; 改变T t; to T* t; T* t; and the compiler wouldn't complain. 并且编译器不会抱怨。

Edit: 编辑:

This does not explain why h() compiles, yet g() does not. 这并不能解释h()编译的原因,但g()却没有。

This example compiles fine: 这个例子很好编译:

#include <memory>

class C;
std::unique_ptr<C> pC;

C& f() {
    if (!pC) throw 0; // OK, even though C is incomplete
    return *pC;         // OK, even though C is incomplete
}

template <class T>
class X
{
    T t;
};

std::unique_ptr<X<C>> pX;

typename std::add_lvalue_reference<X<C>>::type DoSomeStuff() // exact copy of operator*
{
    return (*pX.get());
}

void g() {
    if ((bool)pX) return;
}

class C {};

int main()
{
    auto z = DoSomeStuff();
}

Which makes it even more interesting as this mimics the operator* but does compile. 这使得它更有趣,因为这模仿了operator* ,但进行编译。 Removing the ! 删除! from the expression also works. 从表达也有效。 This seems to be a bug in multiple implementations (MSVC, GCC, Clang). 这似乎是多个实现中的错误(MSVC,GCC,Clang)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM