简体   繁体   English

VS2019 中的 std::string 分配问题,在 VS2015 中运行良好

[英]std::string assignment issue in VS2019, where it is working fine in VS2015

I am having an issue std::string assignment with the below code in VS2019, where it is working fine in VS2015.我在 VS2019 中使用以下代码分配 std::string 时遇到问题,它在 VS2015 中运行良好。 I am using Visual Studio 2019 (v142) as Platform Toolset in VS2019 and Visual Studio 2015 (v140) as Platform Toolset in VS2015.我在 VS2019 中使用Visual Studio 2019 (v142)作为平台工具集,在 VS2015 中使用Visual Studio 2015 (v140)作为平台工具集。

class Test
{
public:
    Test()
    {
        m_DisplayName = "";
        m_Type = "";
    }
    std::string m_DisplayName;
    std::string m_Type;
};

void CMFCApplication2Dlg::TestFunction()
{
    // Sample Data setting in vector
    std::vector<Test> vTest;
    Test obj;
    obj.m_DisplayName = "Nam1gd";
    obj.m_Type = "t1";
    vTest.emplace_back(obj);

    Test obj2;
    obj2.m_DisplayName = "Nam2";
    obj2.m_Type = "t2";
    vTest.emplace_back(obj2);

    VARIANT vtResult_o;
    VariantInit(&vtResult_o);
    vtResult_o.vt = VT_ARRAY | VT_UI1;
    int usrCount = vTest.size();
    ULONG nBufferSize = usrCount * sizeof(Test);

    // Define a safe array of usrCount Item and Starting index as 0 
    SAFEARRAYBOUND safeBounds = { nBufferSize, 0 };

    //Create the Safe Array passing it the bounds 
    SAFEARRAY* pSafeArray = SafeArrayCreate(VT_UI1, 1, &safeBounds);
    Test* vTestArray = nullptr;
    SafeArrayAccessData(pSafeArray, (void**)&vTestArray);

    // Data setting to Safe array
    for (int nIdx = 0; nIdx < usrCount; nIdx++)
    {
        // ******VALUES ASSIGNING CORRECTLY in VS 2015 ******/
        // ******JUNK VALUE COMES IN THIS ASSIGNMENT in VS 2019 ******/
        vTestArray[nIdx].m_DisplayName = vTest[nIdx].m_DisplayName;
        vTestArray[nIdx].m_Type = vTest[nIdx].m_Type;
    }

    /* decrement the lock count*/
    SafeArrayUnaccessData(pSafeArray);
    vtResult_o.parray = pSafeArray;
}

Since the string value in the vTest[nIdx].m_DisplayName is small it is stored in the small buffer in s._Bx._Buf .由于vTest[nIdx].m_DisplayName中的字符串值很小,因此它存储在s._Bx._Buf的小缓冲区中。 In VS2015 while assigning, the small buffer in the source string is copied to the destination string's small buffer.在VS2015中分配时,源字符串中的小缓冲区被复制到目标字符串的小缓冲区中。

 for (int nIdx = 0; nIdx < usrCount; nIdx++)
 {
     // ******VALUES ASSIGNING CORRECTLY in VS 2015 ******/
     // ******JUNK VALUE COMES IN THIS ASSIGNMENT in VS 2019 ******/
     vTestArray[nIdx].m_DisplayName = vTest[nIdx].m_DisplayName;
     vTestArray[nIdx].m_Type = vTest[nIdx].m_Type;
 }

源字符串 However, in VS2019, it is observed that the during assignment the destination's buffer ( s._Bx._Buf ) is updated with junk value and the actual value is updated to the heap s._Bx._ptr .但是,在 VS2019 中,可以观察到在分配期​​间,目标缓冲区 ( s._Bx._Buf ) 会使用垃圾值更新,而实际值会更新到堆s._Bx._ptr

Ideally since s._Bx is a union either the s._Bx._Buf or s._Bx._ptr should be present.理想情况下,因为s._Bx是一个联合体,所以应该存在s._Bx._Bufs._Bx._ptr Please find the image below.请在下面找到图片。 目的地

[Workaround] [解决方法]

However if I do a std::string casting in VS2019 it is assigning the std::string's small buffer in the source to the small buffer in the destination.但是,如果我在 VS2019 中进行 std::string 转换,它将源中的 std::string 小缓冲区分配给目标中的小缓冲区。

for (int nIdx = 0; nIdx < usrCount; nIdx++)
{
    vTestArray[nIdx].m_DisplayName =(std::string)(vTest[nIdx].m_DisplayName);
    vTestArray[nIdx].m_Type = (std::string)(vTest[nIdx].m_Type);
}

I will appreciate if some one can help me to understand why this difference in VS2015 and VS2019!如果有人能帮助我理解为什么 VS2015 和 VS2019 存在这种差异,我将不胜感激!

You are treating the array you obtained as if it contained alive Test objects, which it doesn't.您将获得的数组视为包含活动的Test对象,但事实并非如此。 That causes undefined behavior.这会导致未定义的行为。

Instead of assigning directly, which pretends that there is already an alive object, you should use placement-new to create and start the lifetime of the Test object explicitly first:与其直接分配,它假装已经存在一个活着的对象,你应该首先使用placement-new来显式地创建和启动Test对象的生命周期:

auto obj = new(vTestArray + nIdx) Test;
obj->m_DisplayName = vTest[nIdx].m_DisplayName;
obj->m_Type = vTest[nIdx].m_Type;

and don't forget to destroy the objects when you are done with them with explicit destructor calls:并且不要忘记在使用显式析构函数调用完成对象时销毁它们:

for (int nIdx = 0; nIdx < usrCount; nIdx++) {
    vTestArray[nIdx].~Test();
}

(Technically there might be some minor issue here requiring some additional std::launder call on vTestArray if we read the standard strictly and SafeArrayAccessData would not be considered to produce a "pointer to a suitable created object" under the C++20 implicit object creation rules.) (从技术上讲,如果我们严格阅读标准,这里可能存在一些小问题,需要对vTestArray进行一些额外的std::launder调用,并且SafeArrayAccessData不会被视为在 C++20 隐式对象下生成“指向合适创建的对象的指针”创建规则。)


There is also a more minor issue in that the cast in SafeArrayAccessData(pSafeArray, (void**)&vTestArray);还有一个更小的问题是SafeArrayAccessData(pSafeArray, (void**)&vTestArray); is wrong.是错的。 You cannot reinterpret a pointer to Test* as a pointer to void* .您不能将指向Test*的指针重新解释为指向void*的指针。 That is an aliasing violation.这是一个混叠违规。 You should use an intermediate pointer of the correct type:您应该使用正确类型的中间指针:

void* vTestArrayVoid = nullptr;
SafeArrayAccessData(pSafeArray, &vTestArray);
auto vTestArray = reinterpret_cast<Test*>(vTestArrayVoid);

(Please note that there should not be a std::launder call here against my advice in a previous edit, since there is no Test object in its lifetime yet.) (请注意,这里应该有一个std::launder调用反对我在以前的编辑中的建议,因为在它的生命周期中还没有Test对象。)


I am also not sure what the intended use of the array is, but you should be aware that the strings will generally allocate additional memory that is not part of the array.我也不确定数组的预期用途是什么,但您应该知道字符串通常会分配不属于数组的额外内存。 So this is not a reliable way to eg share memory between processes.因此,这不是一种可靠的方式,例如在进程之间共享内存。

But if you don't have some specific use case in mind (and I might just not be aware here of the obvious if it is Microsoft-specific) then there would be no reason to make it so complicated when std::vector , potentially reference-counted via std::shared_ptr , already does all of this automatically.但是,如果您没有考虑到某些特定的用例(如果它是 Microsoft 特定的,我可能只是没有意识到这一点),那么当std::vector可能时,没有理由让它变得如此复杂通过std::shared_ptr进行引用计数,已经自动完成了所有这些。

Doing it manually in the way I suggested is also not exception-safe, which a std::vector would be.以我建议的方式手动执行它也不是异常安全的,而std::vector就是这样。 If an exception is thrown during construction of one of the Test objects, then you will forget the destruction of the already-constructed Test objects with this naive implementation.如果在构建其中一个Test对象期间抛出异常,那么您将忘记使用这种幼稚的实现来销毁已经构建的Test对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM