C2027:使用未定义类型Foo :: Impl

时间:2018-09-20 05:08:39

标签: c++

全部

class __declspec(dllexport) MyClass
{
protected:
    struct Impl;
    Impl *pimpl;
public:
    Impl &GetStruct() { return pimpl; }
    const std::wstring &GetName() { return pimpl->m_name; };
};

struct MyClass::Impl
{
    std::wstring m_name;
};

GetName()引发错误而GetStruct()不会引发错误吗?以及如何修改代码以进行编译?

TIA !!

3 个答案:

答案 0 :(得分:2)

问题是C ++解析器从上到下工作。在MyClass声明的末尾,嵌套Impl尚未完全定义。它被声明(“有一个Impl结构”),但是完整的定义(“ Impl结构如下……”)仅在以后出现。但是,为了生成pimpl->m_name的代码,编译器需要了解更多,尤其是需要知道m_name是什么类型,以及它所在的包含Impl的偏移量是什么

简而言之,您偶然发现了PIMPL惯用语的一个众所周知的限制,即您不能像以前那样使用内联函数。

答案 1 :(得分:0)

在您请求pimpl->m_name时,尚不知道m_name所指向的事物中有一个名为pimpl的事物。从技术上讲,pimpl不完整类型的指针。看到其定义后,它便完成了。

因此,您需要将GetName的定义移出tye类,并移至MyClass::Impl的定义之后。

答案 2 :(得分:0)

实际上,我打算将MCVE添加到Ulrichs sufficient answer,但是在尝试使其在coliru上运行时,我发现了更多问题:

  1. MyClass::GetStruct()的返回类型为Impl&,但pimpl之后的return的类型为Impl*,这是不兼容的。

  2. MyClass::GetName()返回const std::string&,但它本身不是常量。 (我不确定是否允许这样做。我永远不会这样做。)

  3. 我添加了一个构造函数/析构函数,因为pimpl必须使用实例进行初始化。

  4. 我删除了副本构造函数和副本分配,因为默认实现会破坏复制pimpl指针的实例。

  5. 我转移了MyClass::GetName()的实现以解决Ulrich所描述的问题。

固定的示例代码:

#include <iostream>
#include <string>

class MyClass
{
protected:
    struct Impl;
    Impl *pimpl;
public:
    MyClass();
    ~MyClass();
    MyClass(const MyClass&) = delete;
    MyClass& operator=(const MyClass&) = delete;
#if 0 // WRONG:
    Impl& GetStruct() { return pimpl; }
    const std::string &GetName() { return pimpl->m_name; }
#else // BETTER:
    Impl& GetStruct() { return *pimpl; }
    const std::string& GetName() const;
#endif // 0

};

struct MyClass::Impl
{
    std::string m_name;
};

MyClass::MyClass(): pimpl(new Impl()) { }

MyClass::~MyClass() { delete pimpl; }

// Implementation after MyClass::Impl is defined
const std::string& MyClass::GetName() const { return pimpl->m_name; }

int main()
{
  MyClass myObj;
  std::cout << "myObj.GetName(): '" << myObj.GetName() << "'\n";
  return 0;
}

输出:

myObj.GetName(): ''

Live Demo on coliru

注意:

我将std::w_string替换为std::string,我认为这是无关紧要的更改。 (我没有使用std::w_string的经验,但我想,如果没有替换,它不会有任何改变。)