使用marshal_cppstd.h
和msclr::interop::marshal_as<>
我可以将托管字符串封送到std::string
,如下所示:
String^ managed = "test";
std::string unmanaged = marshal_as<std::string>(managed);
现在当managed
是我正在编写代码的类的成员时,我在执行此操作时出错:
std::string unmanaged = marshal_as<std::string>(this->managed);
错误说:
没有重载函数的实例“marshal_as”匹配参数列表
或编译器错误C2665:
'msclr :: interop :: marshal_as':3个重载中没有一个可以转换所有参数类型
当我更改代码以使用辅助变量时,它可以工作:
String^ localManaged = this->managed;
std::string unmanabed = marshal_as<std::string>(localManaged);
这里必须有一些隐式投射,不是吗?为什么会发生这种情况以及如何使简单的单行工作?
答案 0 :(得分:7)
是的,这是一个非常糟糕的错误消息,并没有帮助您发现真正的问题。模板错误消息通常很难理解。它可以使用一些repro代码:
#include "stdafx.h"
#include <string>
#include <msclr\marshal_cppstd.h>
using namespace System;
using namespace msclr::interop;
ref class Example {
String^ managed;
public:
void test() {
auto bad = marshal_as<std::string>(this->managed); // C2665
auto copy = this->managed;
auto good = marshal_as<std::string>(copy);
}
};
您必须查看“输出”窗口以查看编译器正在努力尝试查找marshal_as&lt;&gt;的版本。与参数类型匹配的模板函数。你会看到它考虑两个模板特化,但不你想要的那个。这是:
template <class _To_Type, class _From_Type>
inline _To_Type marshal_as(const _From_Type&);
_From_Type&
参数是麻烦制造者,请注意它是一个非托管参考,&
。与跟踪参考相反,%
。或者只是简单地^
作为一个引用类型,如System :: String。
很难看出,引用this->managed
与copy
之间存在巨大差异。在像Example ^这样的对象中,this
指针不稳定。它的值可以在此代码运行时更改,在触发垃圾收集时发生。在大多数程序中不太可能,但不是零。当程序中的另一个线程从GC堆分配并触发集合时发生。这种事情每年发生一次。
如果收集发生就像marshal_as&lt;&gt;()正在完成它的工作那将是非常灾难性的。 GC压缩堆后,非托管引用变为无效并指向垃圾。 C ++ / CLI编译器不能让这种情况发生,因此它不会将this->managed&
视为_From_Type&
的有效替代。从来没有看过它。模板专业化也无法与之匹敌,C2665是不可避免的结果。
与copy
参数的重大区别在于它的地址始终是稳定的。在未经优化的代码中存储在堆栈帧中,通常在优化器完成后在CPU寄存器中存储。因此copy&
是_From_Type&
的有效替代品,编译器可以毫无困难地生成模板代码。
因此,您找到的解决方法完全有效,并且是执行此操作的最佳方式。如果编译器只是为我们这样做了会很好,但事实并非如此。别名问题也不好,有时您必须将值复制回来。您只需要了解编写C ++ / CLI代码以及混合托管代码和本机代码的结果,您肯定有一天会再次遇到它。