【问题标题】:"cannot access private member'" error only when class has export linkage仅当类具有导出链接时出现“无法访问私有成员”错误
【发布时间】:2012-02-01 18:16:50
【问题描述】:

我最近不得不更改几个类的链接规范并遇到了问题。其中两个类包含 std::mapstd::unique_ptr 作为值类型。更改链接后,编译器开始抱怨“无法访问在类 'std::unique_ptr<_ty>' 中声明的私有成员”错误。

有谁知道为什么只有在提供出口规范或有解决方案时才会发生这种情况?

示例代码:

#include <map>

struct SomeInterface
{
    virtual ~SomeInterface() = 0;
};


//  This class compiles with no problems
struct LocalClass
{
    std::map<int, std::unique_ptr<SomeInterface>>   mData;
};

//  This class fails to compile
struct __declspec(dllexport) ExportedClass
{
    std::map<int, std::unique_ptr<SomeInterface>>   mData;
};

编译器输出:

c:\program files (x86)\microsoft visual studio 10.0\vc\include\utility(163): error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'
      with
      [
          _Ty=SomeInterface
      ]
      c:\program files (x86)\microsoft visual studio 10.0\vc\include\memory(2347) : see declaration of 'std::unique_ptr<_Ty>::unique_ptr'
      with
      [
          _Ty=SomeInterface
      ]
      c:\program files (x86)\microsoft visual studio 10.0\vc\include\utility(195) : see reference to function template instantiation 'std::_Pair_base<_Ty1,_Ty2>::_Pair_base<const int&,_Ty2&>(_Other1,_Other2)' being compiled
      with
      [
          _Ty1=const int,
          _Ty2=std::unique_ptr<SomeInterface>,
          _Other1=const int &,
          _Other2=std::unique_ptr<SomeInterface> &
      ]
      c:\program files (x86)\microsoft visual studio 10.0\vc\include\xmemory(208) : see reference to function template instantiation 'std::pair<_Ty1,_Ty2>::pair<const _Kty,_Ty>(std::pair<_Ty1,_Ty2> &)' being compiled
      with
      [
          _Ty1=const int,
          _Ty2=std::unique_ptr<SomeInterface>,
          _Kty=int,
          _Ty=std::unique_ptr<SomeInterface>
      ]
      c:\program files (x86)\microsoft visual studio 10.0\vc\include\xmemory(280) : see reference to function template instantiation 'void std::allocator<_Ty>::construct<std::pair<_Ty1,_Ty2>&>(std::pair<_Ty1,_Ty2> *,_Other)' being compiled
      with
      [
          _Ty=std::pair<const int,std::unique_ptr<SomeInterface>>,
          _Ty1=const int,
          _Ty2=std::unique_ptr<SomeInterface>,
          _Other=std::pair<const int,std::unique_ptr<SomeInterface>> &
      ]
      c:\program files (x86)\microsoft visual studio 10.0\vc\include\xtree(592) : see reference to function template instantiation 'void std::_Cons_val<std::allocator<_Ty>,_Ty,std::pair<_Ty1,_Ty2>&>(_Alloc &,std::pair<_Ty1,_Ty2> *,std::pair<_Ty1,_Ty2>)' being compiled
      with
      [
          _Ty=std::pair<const int,std::unique_ptr<SomeInterface>>,
          _Ty1=const int,
          _Ty2=std::unique_ptr<SomeInterface>,
          _Alloc=std::allocator<std::pair<const int,std::unique_ptr<SomeInterface>>>
      ]
      c:\program files (x86)\microsoft visual studio 10.0\vc\include\xtree(1521) : see reference to function template instantiation 'std::_Tree_nod<_Traits>::_Node *std::_Tree_val<_Traits>::_Buynode<std::pair<_Ty1,_Ty2>&>(_Valty)' being compiled
      with
      [
          _Traits=std::_Tmap_traits<int,std::unique_ptr<SomeInterface>,std::less<int>,std::allocator<std::pair<const int,std::unique_ptr<SomeInterface>>>,false>,
          _Ty1=const int,
          _Ty2=std::unique_ptr<SomeInterface>,
          _Valty=std::pair<const int,std::unique_ptr<SomeInterface>> &
      ]
      c:\program files (x86)\microsoft visual studio 10.0\vc\include\xtree(1516) : while compiling class template member function 'std::_Tree_nod<_Traits>::_Node *std::_Tree<_Traits>::_Copy(std::_Tree_nod<_Traits>::_Node *,std::_Tree_nod<_Traits>::_Node *)'
      with
      [
          _Traits=std::_Tmap_traits<int,std::unique_ptr<SomeInterface>,std::less<int>,std::allocator<std::pair<const int,std::unique_ptr<SomeInterface>>>,false>
      ]
      c:\program files (x86)\microsoft visual studio 10.0\vc\include\map(81) : see reference to class template instantiation 'std::_Tree<_Traits>' being compiled
      with
      [
          _Traits=std::_Tmap_traits<int,std::unique_ptr<SomeInterface>,std::less<int>,std::allocator<std::pair<const int,std::unique_ptr<SomeInterface>>>,false>
      ]
      c:\projects\so\so\so.cpp(18) : see reference to class template instantiation 'std::map<_Kty,_Ty>' being compiled
      with
      [
          _Kty=int,
          _Ty=std::unique_ptr<SomeInterface>
      ]

【问题讨论】:

  • 至少,这是一个纯粹的平台和编译器特定的问题。 C++ 没有标准化的 ABI,__declspec(declexport)class 定义的影响完全取决于编译器。

标签: c++ visual-c++ c++11


【解决方案1】:

std::move(iUniquePtr)”经常在某处丢失。

【讨论】:

    【解决方案2】:

    我很久以前就遇到过这个,所以细节有点模糊。

    本质上,当您导出一个类时,您还必须导出所有包含的类——无论是否公开。在您的情况下,这将是 std::mapstd::unique_ptr。我不确定标准库中的类在这里如何表现,这是模糊的部分,但我记得遇到过问题。

    解决方案是导出这些类或使用PIMPL 实现(无论如何这对于导出的类来说都是个好主意)。

    【讨论】:

    • 使用 PIMPL 的 DLL 类接口使事情变得更容易:)
    【解决方案3】:

    由于编译器无法为 ExportedClass 创建复制构造函数和复制赋值运算符,因此给出错误。这将需要复制没有复制构造函数的 unique_ptr 对象(它们是可移动的但不可复制)。

    对于普通类,不会给出错误,因为复制构造函数/赋值实际上并未在任何地方使用。但是,当存在 __declspec(dllexport) 时,所有编译器生成的函数都将被实例化(不确定此处的术语是否正确,但类似:)。

    修复错误的一种方法是为 ExportedClass 定义这两个函数并将它们标记为私有:

    struct __declspec(dllexport) ExportedClass
    {
        std::map<int, std::unique_ptr<SomeInterface>>   mData;
    private:
        ExportedClass(const ExportedClass&) {}
        ExportedClass& operator=(const ExportedClass&) { return *this; }
    };
    

    【讨论】:

    • 当你导出一个类型时,整个公共接口加上内联函数使用的任何东西也需要被导出。有关详细信息,请参阅warning C4251 的文档。
    • 我已经知道 C4251 但是在示例代码中解决它会毫无意义的混乱并且与我的问题无关。
    • 空(没有定义的声明)分配/复制足以解决问题。否则,如果 ExportedClass 有内部类,我们可能必须为所有它们定义复制/分配。但是,如果顶级类的非空副本/分配需要它们,则内部类的复制/分配不能是私有的。
    猜你喜欢
    • 2016-08-18
    • 1970-01-01
    • 2014-01-10
    • 2013-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多