【问题标题】:Incomplete type used in nested name specifier for Pimpl IdiomPimpl Idiom 的嵌套名称说明符中使用的不完整类型
【发布时间】:2018-05-26 16:18:57
【问题描述】:

以下代码出现此错误

嵌套名称说明符中使用的不完整类型“Foo::Pimpl”

另一个Foo.hpp

struct AnotherFoo {
    void methodAnotherFoo(Foo &);
};

另一个Foo.cpp

#include "Foo.hpp"
#include "AnotherFoo.hpp"

void AnotherFoo::methodAnotherFoo(Foo &foo) {
    // here i want to save the function pointer of methodPimpl(), std::function for ex:
    std::function<void(void)> fn = std::bind(&Foo::Pimpl::methodPimpl, foo._pimpl); // <-- Here i am getting the error
}

Foo.hpp

struct Foo {
    Foo();
    class Pimpl;
    std::shared_ptr<Pimpl> _pimpl;
};

Foo.cpp

#include "Foo.hpp"

struct Foo::Pimpl {
    void methodPimpl(void) {}    
};

Foo::Foo() : _pimpl(new Pimpl) {}

main.cpp

#include "Foo.hpp"
#include "AnotherFoo.hpp"

int main() {
    Foo foo;
    AnotherFoo anotherFoo;
    anotherFoo.methodAnotherFoo(foo);
}

谁有解决这个问题的好办法?

我要实现的主要目标是使 methodAnotherFoo 方法的签名从头文件中隐藏。

【问题讨论】:

  • 我认为PIMPL 不太可能需要std::shared_ptrstd::unique_ptr 不是更合适吗?
  • 作为 PImpl 模式的替代方案,您可以使用抽象基类、工厂函数(可能作为类静态函数,它将返回 std::unique_ptr)和包含所有细节的“impl”派生类。缺点是 vtable 查找的额外间接性,但由于 pimpl,您基本上具有相同的成本。

标签: c++ c++11 c++-standard-library pimpl-idiom


【解决方案1】:

您可以访问Foo::Pimpl 详细信息的唯一文件是 Foo.cpp,它是在其中定义的文件。

您可能无法在 AnotherFoo.cpp 中访问它。

您的选择是:

  1. AnotherFoo::methodAnotherFoo 的实现更改为仅使用Foo 的公共接口。

  2. AnotherFoo::methodAnotherFoo的实现移动到Foo.cpp。

【讨论】:

  • " 定义它的文件。"这是 c++17 的新特性吗?
  • @Slava:不,不是。 Foo::Pimpl的实际内容是在Foo.cpp内部定义的,没有其他文件知道Foo::Pimpl的布局,所以Foo.cpp是唯一可以访问Foo::Pimpl成员的文件。
  • @RemyLebea 嗯我错过了一些东西,因为什么时候可以在外部结构之外定义内部结构?
  • @Slava,自 C++98 以来一直存在。
  • @Slava:很长一段时间以来,也许总是。 PIMPL 成语已经存在了很长时间。
【解决方案2】:

如果 AnotherFoo.cpp 需要直接访问实现对象,则必须查看该类型的定义,这是没有办法的。或许可以添加一个“detail/foo.h”标题,供内部使用。

【讨论】:

    【解决方案3】:

    您的 Pimpl 实现不正确。当您尝试从methodAnotherFoo 直接访问它们时,它应该隐藏详细信息。因此,您应该将实现细节设为私有,并提供公共代理方法来操作存储的实现:

    class Foo
    {
        public: Foo();
    
        public: void method(void);
    
        private: class Pimpl;
        private: std::shared_ptr<Pimpl> _pimpl;
    };
    
    // Foo.cpp
    struct Foo::Pimpl
    {
        void methodPimpl(void) {}    
    };
    
    Foo::Foo() : _pimpl(new Pimpl) {}
    
    void Foo::method(void) {_pimpl->method();}
    

    并更改其余代码以利用这些代理方法,而不是挖掘实现细节:

    void AnotherFoo::methodAnotherFoo(Foo &foo)
    {
        std::function<void(void)> fn = std::bind(&Foo::method, foo);
    }
    

    【讨论】:

      【解决方案4】:

      我找到的一个解决方案是将 Pimpl 实现移动到 AnotherFoo.cpp

      【讨论】:

      • “似乎有效”很少(永远不会?)足够好 - 除非您了解 why 并且“有效”与未定义行为无关。 从不在编程中猜测或假设; 知道
      猜你喜欢
      • 1970-01-01
      • 2014-07-28
      • 1970-01-01
      • 1970-01-01
      • 2021-07-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-01
      相关资源
      最近更新 更多