You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实现自定义数组功能时如何避免C++复制消除?

C++模拟JavaScript值的CValue类复制消除问题

我正在用C++实现一个模拟JavaScript值的CValue类,该类可表示多种类型(基础类型、数组、哈希表对象等)。为访问数组元素,CValue提供了返回CValue而非CValue&的operator[]方法:

  • 若调用对象非数组,返回类似JS中undefined的空CValue;
  • 若为有效数组且索引合法,返回包含指向数组元素指针的类引用CValue对象,以此支持arr[0] = 42这类修改原数组元素的操作。

正常场景下复制CValue对象会创建副本,但执行CValue var = arr[0];时,编译器会触发复制消除优化,直接将新建的var替换为operator[]返回的类引用对象,导致修改var时同时修改原数组元素的异常行为:

arr[0] = 42;
CValue var = arr[0];  // var = 42,  arr[0] = 42
var = 100;            // var = 100, arr[0] = 100

我希望仅在此特定场景下避免复制消除,而非全局禁用该优化。以下是最小可复现示例及输出:


cvalue.h

#include <memory>
#include <vector>

class IBaseValue;
class CPrimitive;
class CArray;

// wrapper around IBaseValue (see below) with specific value type
class CTypedValue
{
public:
    enum ValueType
    {
        vtUndefined,
        vtInteger,
        vtArray
    };

public:
    CTypedValue();

public:
    std::shared_ptr<IBaseValue> m_value;
    ValueType m_type;
};

// Container for CTypedValue which is also allows to treat typed value as reference
class CTypedValueContainer
{
public:
    CTypedValueContainer();
    CTypedValueContainer(const CTypedValueContainer& other);

    CTypedValueContainer& operator=(const CTypedValueContainer& other);

public:
    std::shared_ptr<CTypedValue> m_typedValue;
    bool m_isReference;
};

// Main class
class CValue
{
public:
    CValue();
    CValue(int value);
    CValue(const CValue& other);

    ~CValue();

    CValue& operator=(const CValue& other);

    static CValue createArray(int size);

    bool IsUndefined();
    operator int();
    CValue operator[](int index);

private:
    CTypedValueContainer* m_internal;
};

// Base class for value types
class IBaseValue
{
public:
    IBaseValue() = default;
    virtual ~IBaseValue() = default;
};

// Primitive value type (only with int type for MRE)
class CPrimitive : public IBaseValue
{
public:
    CPrimitive(int value);

    int toInt();

private:
    int m_value;
};

// Array value type
class CArray : public IBaseValue
{
public:
    CArray(int size);

    CValue& get(int i);

private:
    std::vector<CValue> m_values;
};

cvalue.cpp

#include "cvalue.h"

CPrimitive::CPrimitive(int value) : m_value(value)
{
}

int CPrimitive::toInt()
{
    return m_value;
}

CArray::CArray(int size) : m_values(size)
{
}

CValue& CArray::get(int i)
{
    return m_values[i];
}

CTypedValue::CTypedValue() : m_type(vtUndefined)
{
}

CTypedValueContainer::CTypedValueContainer() : m_typedValue(new CTypedValue()), m_isReference(false)
{
}

CTypedValueContainer::CTypedValueContainer(const CTypedValueContainer& other) : CTypedValueContainer()
{
    *this = other;
}

CTypedValueContainer& CTypedValueContainer::operator=(const CTypedValueContainer& other)
{
    if (other.m_isReference)
        m_typedValue = other.m_typedValue;
    else
        *m_typedValue = *other.m_typedValue;
    return *this;
}

CValue::CValue() : m_internal(new CTypedValueContainer)
{
}

CValue::CValue(int value) : CValue()
{
    m_internal->m_typedValue->m_value = std::make_shared<CPrimitive>(value);
    m_internal->m_typedValue->m_type = CTypedValue::vtInteger;
}

CValue::CValue(const CValue& other) : m_internal(new CTypedValueContainer(*other.m_internal))
{
}

CValue::~CValue()
{
    delete m_internal;
}

CValue CValue::createArray(int size)
{
    CValue ret;
    ret.m_internal->m_typedValue->m_value = std::make_shared<CArray>(size);
    ret.m_internal->m_typedValue->m_type = CTypedValue::vtArray;
    return ret;
}

CValue& CValue::operator=(const CValue& other)
{
    *m_internal = *other.m_internal;
    return *this;
}

bool CValue::IsUndefined()
{
    return m_internal->m_typedValue->m_type == CTypedValue::vtUndefined;
}

CValue::operator int()
{
    if (m_internal->m_typedValue->m_type != CTypedValue::vtInteger)
        return 0;
    return static_cast<CPrimitive*>(m_internal->m_typedValue->m_value.get())->toInt();
}

CValue CValue::operator[](int index)
{
    if (m_internal->m_typedValue->m_type != CTypedValue::vtArray)
        return CValue();
    CValue& value = static_cast<CArray*>(m_internal->m_typedValue->m_value.get())->get(index);
    value.m_internal->m_isReference = true;
    return value;
}

main.cpp

#include <iostream>

#include "cvalue.h"

int main()
{
    // Normal copy
    CValue var1 = 42;
    CValue var2 = var1;
    std::cout << "var1 = " << (int)var1 << ", var2 = " << (int)var2 << std::endl;
    var2 = 100;
    std::cout << "var1 = " << (int)var1 << ", var2 = " << (int)var2 << std::endl;
    // Copy of array element
    CValue arr = CValue::createArray(1);
    arr[0] = 42;
    CValue var = arr[0];
    std::cout << "var = " << (int)var << ",  arr[0] = " << (int)arr[0] << std::endl;
    var = 100;
    std::cout << "var = " << (int)var << ", arr[0] = " << (int)arr[0] << std::endl;

    return 0;
}

编译输出

使用MinGW GCC编译器编译:

  • 执行g++ main.cpp cvalue.cpp的输出:
var1 = 42, var2 = 42
var1 = 42, var2 = 100
var = 42,  arr[0] = 42
var = 100, arr[0] = 100
  • 添加-fno-elide-constructors参数编译的输出(期望结果):
var1 = 42, var2 = 42
var1 = 42, var2 = 100
var = 42,  arr[0] = 42
var = 100, arr[0] = 42

内容的提问来源于stack exchange,提问作者asethone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 20:17:01