实现自定义数组功能时如何避免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
相关产品推荐
相关产品推荐

