5

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).

Pablo H
  • 609
  • 4
  • 22
  • You'll have to file code generator bugs at connect.microsoft.com, not the same you found earlier. Not so sure they'll consider fixing it, this is an excessively ugly way to use standard C++ template classes. Beyond the clearly visible double-thunking in the call stack, such code really needs the unmanaged optimizer to get efficient. You already know the workaround, move into another translation unit or use #pragma managed(push, off) judiciously. – Hans Passant Aug 13 '16 at 09:48
  • 1
    This apparently also affects passing unique_ptr to constructors of derived classes (VS 2015 Update 3). – tomi.lee.jones Mar 29 '17 at 21:26

0 Answers0