在使用C++/CLI中的unique_ptr时出现链接器错误

10

我目前正在将我的auto_ptr实例转换为unique_ptr,但我遇到了一个问题。在C++部分的代码中它运行得很好,但是当我在我的托管C++/CLI层(软件同时使用C#和C++)中进行时,我会遇到链接错误。它可以编译,但在链接时会出现错误。使用auto_ptr从未出现过任何问题。

我目前正在使用Visual Studio 2010。有人知道在C++/CLI中使用unique_ptr是否存在任何问题吗?

我尝试在下面的代码片段中总结我的问题,但请注意,下面的代码实际上可以编译和工作(我检查了指针所有权的移动是否正确)。编译这个代码时我没有得到链接错误,但下面的代码是纯C++而不是C++/CLI。我只是想有一个最小的例子来说明代码的构造方式,以便链接器错误更容易理解。

#include "stdafx.h"
#include <vector>
#include <memory>
#include <utility>

using namespace std;

namespace Test {

template< class T >
struct LinAlgPoint3 {
  LinAlgPoint3() { x = y = z = 0; };

  union {
    struct {T x,y,z;} ;
    T data_[3];
  };
};

class ContainerClass
{
public:
  void setUniquePtr(
    unique_ptr< vector< LinAlgPoint3< float > > > newUniquePtr1 ,
    unique_ptr< vector< unsigned char > > newUniquePtr2 )
  {
    m_uniquePtr1 = move(newUniquePtr1);
    m_uniquePtr2 = move(newUniquePtr2);
  }

private:
  unique_ptr< vector< LinAlgPoint3< float > > > m_uniquePtr1;
  unique_ptr< vector< unsigned char > > m_uniquePtr2;
};

int main(int argc, char** argv)
{
  auto pos = unique_ptr< vector< LinAlgPoint3< float > > >( new vector< LinAlgPoint3< float > >() );
  auto name = unique_ptr< vector< unsigned char > >(new vector< unsigned char >());
  ContainerClass container;
  container.setUniquePtr(move(pos), move(name));
}

} //namespace Test

我在链接时遇到的错误如下:
error LNK2028: unresolved token (0A0018A5) "private: __cdecl std::unique_ptr<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > >,struct std::default_delete<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > > > >::unique_ptr<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > >,struct std::default_delete<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > > > >(class std::unique_ptr<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > >,struct std::default_delete<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > > > > const &)" (??0?$unique_ptr@V?$vector@U?$LinAlgPoint3@M@Test@@V?$allocator@U?$LinAlgPoint3@M@Test@@@std@@@std@@U?$default_delete@V?$vector@U?$LinAlgPoint3@M@Test@@V?$allocator@U?$LinAlgPoint3@M@Test@@@std@@@std@@@2@@std@@$$FAEAA@AEBV01@@Z) referenced in function "public: static void __clrcall std::unique_ptr<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > >,struct std::default_delete<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > > > >::<MarshalCopy>(class std::unique_ptr<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > >,struct std::default_delete<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > > > > *,class std::unique_ptr<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > >,struct std::default_delete<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > > > > *)" (?<MarshalCopy>@?$unique_ptr@V?$vector@U?$LinAlgPoint3@M@Test@@V?$allocator@U?$LinAlgPoint3@M@Test@@@std@@@std@@U?$default_delete@V?$vector@U?$LinAlgPoint3@M@Test@@V?$allocator@U?$LinAlgPoint3@M@Test@@@std@@@std@@@2@@std@@$$FSMXPEAV12@0@Z)
1>TestClass.obj : error LNK2028: unresolved token (0A0018A6) "private: __cdecl std::unique_ptr<class std::vector<unsigned char,class std::allocator<unsigned char> >,struct std::default_delete<class std::vector<unsigned char,class std::allocator<unsigned char> > > >::unique_ptr<class std::vector<unsigned char,class std::allocator<unsigned char> >,struct std::default_delete<class std::vector<unsigned char,class std::allocator<unsigned char> > > >(class std::unique_ptr<class std::vector<unsigned char,class std::allocator<unsigned char> >,struct std::default_delete<class std::vector<unsigned char,class std::allocator<unsigned char> > > > const &)" (??0?$unique_ptr@V?$vector@EV?$allocator@E@std@@@std@@U?$default_delete@V?$vector@EV?$allocator@E@std@@@std@@@2@@std@@$$FAEAA@AEBV01@@Z) referenced in function "public: static void __clrcall std::unique_ptr<class std::vector<unsigned char,class std::allocator<unsigned char> >,struct std::default_delete<class std::vector<unsigned char,class std::allocator<unsigned char> > > >::<MarshalCopy>(class std::unique_ptr<class std::vector<unsigned char,class std::allocator<unsigned char> >,struct std::default_delete<class std::vector<unsigned char,class std::allocator<unsigned char> > > > *,class std::unique_ptr<class std::vector<unsigned char,class std::allocator<unsigned char> >,struct std::default_delete<class std::vector<unsigned char,class std::allocator<unsigned char> > > > *)" (?<MarshalCopy>@?$unique_ptr@V?$vector@EV?$allocator@E@std@@@std@@U?$default_delete@V?$vector@EV?$allocator@E@std@@@std@@@2@@std@@$$FSMXPEAV12@0@Z)
1>TestClass.obj : error LNK2019: unresolved external symbol "private: __cdecl std::unique_ptr<class std::vector<unsigned char,class std::allocator<unsigned char> >,struct std::default_delete<class std::vector<unsigned char,class std::allocator<unsigned char> > > >::unique_ptr<class std::vector<unsigned char,class std::allocator<unsigned char> >,struct std::default_delete<class std::vector<unsigned char,class std::allocator<unsigned char> > > >(class std::unique_ptr<class std::vector<unsigned char,class std::allocator<unsigned char> >,struct std::default_delete<class std::vector<unsigned char,class std::allocator<unsigned char> > > > const &)" (??0?$unique_ptr@V?$vector@EV?$allocator@E@std@@@std@@U?$default_delete@V?$vector@EV?$allocator@E@std@@@std@@@2@@std@@$$FAEAA@AEBV01@@Z) referenced in function "public: static void __clrcall std::unique_ptr<class std::vector<unsigned char,class std::allocator<unsigned char> >,struct std::default_delete<class std::vector<unsigned char,class std::allocator<unsigned char> > > >::<MarshalCopy>(class std::unique_ptr<class std::vector<unsigned char,class std::allocator<unsigned char> >,struct std::default_delete<class std::vector<unsigned char,class std::allocator<unsigned char> > > > *,class std::unique_ptr<class std::vector<unsigned char,class std::allocator<unsigned char> >,struct std::default_delete<class std::vector<unsigned char,class std::allocator<unsigned char> > > > *)" (?<MarshalCopy>@?$unique_ptr@V?$vector@EV?$allocator@E@std@@@std@@U?$default_delete@V?$vector@EV?$allocator@E@std@@@std@@@2@@std@@$$FSMXPEAV12@0@Z)
1>TestClass.obj : error LNK2019: unresolved external symbol "private: __cdecl std::unique_ptr<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > >,struct std::default_delete<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > > > >::unique_ptr<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > >,struct std::default_delete<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > > > >(class std::unique_ptr<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > >,struct std::default_delete<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > > > > const &)" (??0?$unique_ptr@V?$vector@U?$LinAlgPoint3@M@Test@@V?$allocator@U?$LinAlgPoint3@M@Test@@@std@@@std@@U?$default_delete@V?$vector@U?$LinAlgPoint3@M@Test@@V?$allocator@U?$LinAlgPoint3@M@Test@@@std@@@std@@@2@@std@@$$FAEAA@AEBV01@@Z) referenced in function "public: static void __clrcall std::unique_ptr<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > >,struct std::default_delete<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > > > >::<MarshalCopy>(class std::unique_ptr<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > >,struct std::default_delete<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > > > > *,class std::unique_ptr<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > >,struct std::default_delete<class std::vector<struct LinAlgPoint3<float>,class std::allocator<struct LinAlgPoint3<float> > > > > *)" (?<MarshalCopy>@?$unique_ptr@V?$vector@U?$LinAlgPoint3@M@Test@@V?$allocator@U?$LinAlgPoint3@M@Test@@@std@@@std@@U?$default_delete@V?$vector@U?$LinAlgPoint3@M@Test@@V?$allocator@U?$LinAlgPoint3@M@Test@@@std@@@std@@@2@@std@@$$FSMXPEAV12@0@Z)
1>D:\Test\Test.dll : fatal error LNK1120: 4 unresolved externals

正如你所看到的(如果你能忍受那些极其糟糕的信息),有一些关于MarshalCopy的引用,这让我担心C++/CLI可能还不支持unique_ptr。

软件的布局是

C# executable -> C++/CLI translation layer (dll) -> C++ dll

所以使用unique_ptr编译C++ dll没有问题,但是C++/CLI dll无法正确链接。

我忘记提到一些非常重要的事情:如果我使用unique_ptr指向一个更简单的数据类型,比如字符串,它可以成功链接。例如:

  auto string1= unique_ptr< string >(new string(20000, 'S'));
  auto string2 = unique_ptr< string >(new string(20000, 'A'));
  string1= std::move(string2);

我也尝试确保使用变量,以便编译器不会将其优化掉。

编辑:我刚刚测试了添加另一个接受unique_ptr<string>的外部函数,并尝试发送上面的string1,但仍然出现错误!因此,问题必须在生成的DLL之间,因为std::move()在每个文件/类中都可以正常工作。


当我们只看到错误而不是创建错误的代码时,诊断问题很困难。你说这个C++代码可以工作,但你实际的C++/CLI代码却不能。你能写一些展示问题的C++/CLI代码吗? - David Yaw
我可以尝试... 我这些日子里没有从头开始写过太多的C++/CLI :) - AzP
注释掉所有的移动调用以获得干净的编译。移动语义是由C++编译器实现的C++11特性,而不是C++/CLI编译器。您可以在connect.microsoft.com上发布以摇晃该树。 - Hans Passant
1
我认为它不可能通过干净的编译。如果C++/CLI编译器没有实现移动语义,那么unique_ptr就不可能工作。 - Puppy
然后我得到了error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>',就像我在上面的小例子中添加move()调用之前遇到的一样。还有一件事我忘了提到的是,如果我使用unique_ptr<double>而不是上面更高级的类型,代码可以编译通过。不过我需要再次确认一下。 - AzP
编辑了问题以添加一些重要信息。 - AzP
3个回答

9

嗯,现在不知道它的相关性如何,但我在C++/CLI中遇到了完全相同的问题,并通过采用r-value引用而不是值来解决它。我的意思是:

void setUniquePtr(unique_ptr&& a, unique_ptr&& b)

这样编译就可以通过,尽管这不是做事情最干净的方式。


这应该被标记为答案。对我有用。 - AarCee

2

通过阅读这个复杂的错误消息,我认为它在抱怨LinAlgPoint3结构体没有拷贝构造函数。尝试实现拷贝构造函数,以及===操作符,看看是否能解决问题。


谢谢你的好建议。你是对的,这个类缺少一个赋值运算符和复制构造函数(它已经有==了),但添加它们并没有解决问题.. :( - AzP
我刚刚在问题的末尾添加了更多信息。 - AzP

1

你不能在动态链接库之间传递C++对象并期望它正常工作。

内存布局可能在不同的模块中有所不同。几乎肯定使用了不同的分配器(这会破坏任何拥有内存的C++类型,包括stringvectorunique_ptr)。

使用指向纯虚基类的指针可以帮助解决这个问题。

或者,如果您有C++部分的源代码,请尝试将C++和C++/CLI链接到单个DLL中,而不是引用单独的DLL中的C++代码。


但 unique_ptr 隐式地获取指向析构函数的指针,以便根据类型调用正确的析构函数。 - AzP
@AzP:它不仅需要指向析构函数的指针,而且对于每个删除器都进行了专门化。这会在模块边界上引起问题。 - Ben Voigt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接