c – unique_ptr何时需要完整的类型?

在下面的代码中,函数f()可以调用运算符bool()和operator *()的成员函数unique_ptr< C>对于不完整的类C,但是当函数g()尝试为unique_ptr< X>>调用那些相同的成员函数时,编译器突然想要一个完整的类型并尝试实例化X C,然后X C失败.由于某些原因,unique_ptr< X> :: get()不会导致模板实例化并正确编译,如在函数h()中可以看到的那样.这是为什么?什么使get()与运算符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 {};

解决方法

这里是一个设计简单的例子,仅使用我们自己的类型:
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
}

问题是,当我们对foo进行不合格的调用时,而不是对:: foo进行限定的调用,或者调用成员函数Ptr< T> :: foo(),我们触发参数相关的查找.

ADL将在类模板特殊化中查找模板类型的关联命名空间,这将触发隐式模板实例化.需要触发模板实例化以便执行ADL查找,因为例如Wrap< Incomplete>可以声明一个需要调用的朋友void foo(Ptr< Wrap< Incomplete>>).或包裹<不完整>可能有依赖基地,其名称空间也需要考虑.此时的实例化使得代码不正确,因为Incomplete是一个不完整的类型,并且您不能拥有不完整类型的成员.

回到原来的问题,对!pX和* pX的调用调用ADL,这导致X C的实例化.这是不正式的.对pX.get()的调用不会调用ADL,这就是为什么一个工作正常.

更多细节见this answer,也是CWG 557.

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...