编组来自 class 成员的托管字符串

Marshalling managed string from class member

使用 marshal_cppstd.hmsclr::interop::marshal_as<> 我可以像这样将托管字符串编组为 std::string

String^ managed = "test";
std::string unmanaged = marshal_as<std::string>(managed);

现在,当 managed 是 class 的成员时,我正在编写代码,我在简单地执行此操作时遇到错误:

std::string unmanaged = marshal_as<std::string>(this->managed);

错误说:

no instance of overloaded function "marshal_as" matches the argument list

或作为编译器错误 C2665:

'msclr::interop::marshal_as': none of the 3 overloads could convert all the argument types

当我更改代码以使用辅助变量时,它起作用了:

String^ localManaged = this->managed;
std::string unmanabed = marshal_as<std::string>(localManaged);

这里一定有一些隐式转换,不是吗?为什么会发生这种情况以及如何使简单的单行工作?

是的,这是一条非常糟糕的错误消息,无法帮助您发现真正的问题。模板错误消息通常很难理解。它可以使用一些重现代码:

#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);
    }
};

您必须查看输出 window 才能看到编译器努力寻找与参数类型匹配的 marshal_as<> 模板函数版本。您会看到它考虑了两个模板专业化,但不是您想要的那个。即:

template <class _To_Type, class _From_Type>
inline _To_Type marshal_as(const _From_Type&);

_From_Type& 参数是麻烦制造者,请注意它是一个 unmanaged 引用,&。与跟踪参考相反,%。或者只是简单的 ^ 就像 System::String.

这样的引用类型

很难看出,this->managedcopy 之间存在巨大差异。在像 Example^ 这样的对象中,this 指针不稳定。当此代码为 运行 时,它的值可能会更改,发生在触发垃圾收集时。大多数程序中的可能性不大,但不是零。当程序中的另一个线程从 GC 堆分配并触发收集时发生。那种事~一年一次。

如果在 marshal_as<>() 执行其工作时进行收集,那将是非常灾难性的。非托管引用在 GC 压缩堆后变得无效并指向垃圾。 C++/CLI 编译器不允许这种情况发生,因此它不会将 this->managed& 视为 _From_Type& 的有效替代。甚至从来不看它。模板特化也比不过,C2665是必然的结果

copy参数的最大区别是它的地址总是稳定的。以未优化的代码存储在堆栈帧中,通常在优化器完成后存储在 CPU 寄存器中。所以 copy& _From_Type& 的有效替代,编译器可以毫无问题地生成模板代码。

因此,您找到的解决方法是完全有效的,也是实现此目的的最佳方法。如果编译器只是为我们做这件事就好了,但事实并非如此。别名问题也不好,有时您必须将值复制回来。关于编写 C++/CLI 代码以及混合托管代码和本机代码的结果,您必须了解的一些内容,总有一天您一定会再次遇到它。