The code included below works fine when compiled as C++, but causes (for example) access violation (System.AccessViolationException) when compiled as C++/CLI (/clr
). In both cases the code compiles and links without errors or warnings (/W4
).
c->g(std::move(p));
(excerpt)
The problem seems to be that the temporary unique_ptr created por the argument is destroyed before calling the function. This happens when calling virtual functions (C::g
and C::k
), but does not happen when calling the non-virtual functions (C::h
and ::g
). It happens whether the calling code is part of an unmanaged class (struct
) or a managed one (ref struct
). It does not happen if I wrap the class with #pragma unmanaged
. (The example code compiles as unmanaged C++ only when not using C++/CLI-only features, of course.)
Somewhat related: Linker error when using unique_ptr in C++/CLI, but my code links fine.
Question(s): Is this a bug in the compiler? If so, Is this a known bug? If not, is it a known limitation of C++/CLI? Or else, a mistake in the code? If it is a compiler bug or limitation, is there a (preferably simple) workaround without changing the function signature (as suggested in this answer)?
(Generated code: Although I am familiar with assembler, I did not understand, in the disassembly of the C++/CLI-compiled process shown by the debugger, where the call to the virtual function is. When debugging I end up only stepping into all the unique_ptr stuff, but not the function call...)
(Some background: The calling code, managed classes, is intended as a bridge between C# code and unmanaged C++ libraries.)
Using:
Microsoft Visual Studio Professional 2015 Version 14.0.25425.01 Update 3;
Visual Studio 2015 (v140) toolset;
Microsoft .NET Framework Version 4.6.01038
Also failed with:
Version 14.0.24720.00 Update 1
#include <memory>
#include <iostream>
using namespace std;
struct A
{
virtual ~A() { cout << "~A()" << endl; }
};
struct I
{
virtual void g(std::unique_ptr<A> p) = 0;
};
struct C : public I
{
virtual void g(std::unique_ptr<A> p) override { cout << "C::g()" << endl; }
void h(std::unique_ptr<A> p) { cout << "C::h()" << endl; }
virtual void k(std::unique_ptr<A> p) { cout << "C::k()" << endl; }
};
void g(std::unique_ptr<A> p) { cout << "g()" << endl; }
//#pragma unmanaged
/*public ref*/ struct B
{
void bug(I * c) {
std::unique_ptr<A> p(new A);
c->g(std::move(p)); // *p destroyed before reaching g() when /clr
}
void bug2(C * c) {
std::unique_ptr<A> p(new A);
c->k(std::move(p)); // *p destroyed before reaching g() when /clr
}
void ok() {
std::unique_ptr<A> p(new A);
g(std::move(p));
}
void ok(C * c) {
std::unique_ptr<A> p(new A);
c->h(std::move(p));
}
};
//#pragma managed
int main()
{
auto b = /*gcnew*/ new B;
b->ok();
C c;
b->ok(&c);
b->bug(&c);
b->bug2(&c);
return 0;
}
Update: A (very?) similar problem was reported to MS for Visual Studio 2013 Update 1, and allegedly solved (?).
Update: This or a similar problem was reported on Visual Studio Developer Community. They report the problem is still present in VS2015.3 (C++ 19.00.24215.1) and VS2017.6 Preview 5 (C++ 19.13.26127.1).