Search code examples
stlc++-cli

Why does vector.push_back(System::Byte) not compile any more in VC++ 14.29 (C++/CLI)


I have the following code that used to compile and work fine:

std::vector<unsigned char> marshal_as(cli::array<System::Byte>^ const& from) 
{
    std::vector<unsigned char> result;
    result.reserve(from->Length);
    for (int i = 0; i < from->Length; i++)
    {
        result.push_back(from[i]);
    }
    return result;
}

After updating VisualStudio to version 16.10 - which updates the C++ compiler to version 14.29 - the code produces an error:

error C2664: 'void std::vector<unsigned char,std::allocator<_Ty>>::push_back(const _Ty &)': cannot convert argument 1 from 'unsigned char' to 'const _Ty &' with [ _Ty=unsigned char ]

message : An object from the gc heap (element of a managed array) cannot be converted to a native reference

message : see declaration of 'std::vector<unsigned char,std::allocator<_Ty>>::push_back' with [ _Ty=unsigned char ]

Changing the code in the loop body to

    unsigned char b = from[i];
    result.push_back(b);

fixes the problem.

I would like to understand the cause of this error. Is this somehow related to a change due to the C++ 20 standard?


Solution

  • Is this somehow related to a change due to the C++ 20 standard?

    No. While std::vector<>::push() has subtly changed in C++20, it's not a change that materially affects what's going on here, the issue is definitely clr-specific.

    I would like to understand the cause of this error.

    This is almost certainly (see below) an error that was always present in your code, but was not being reported by previous versions of the C++/CLI compiler.

    Consider the following function:

    void foo(const int& v) {
      int* ptr = &v;
      // store ptr somewhere, long-term.
    }
    

    It's obvious that invoking foo() with a reference to a gc-backed int would be a recipe for disaster. Yet that's exactly what result.push_back(from[i]); does.

    Your code "works" because push_back() happens to do nothing with its parameter that causes an issue. However, the compiler is not supposed to know that.

    N.B. I say almost certainly because I'm having a heck of a time tracking down the call signature for cli::array<T>::operator[](std::size_t) const. It's not impossible that it used to return a T and now returns const T%.