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

含const成员变量类的默认移动构造函数行为探究

带const成员的类默认移动构造行为分析

编译环境:C17标准,g(开启-Wall -Wextra编译选项)

测试代码

#include <iostream>
#include <vector>

struct myclass
{
                const int num;
                const std::vector<int> vec;

                // Delete copy constructors
                myclass(const  myclass&)        = delete;
                myclass( myclass&)              = delete;
                // Default move constructor
                myclass( myclass&&)             = default;
};

int main()
{
        myclass A = {1, {2}};

        std::cout << "Before  A: " << &A.vec[0]
                << "\nBefore  A[0]: " << A.vec[0]
                << "\nBefore  A int: " << A.num;

        myclass otherA = std::move(A);

        std::cout << "\n\nAfter moving:\nOrig  A: " << &A.vec[0]
                << "\nOrig  A[0]: " << A.vec[0]
                << "\nOrig  A int: " << A.num;

        std::cout << "\n\nOther A: " << &otherA.vec[0]
                << "\nOther A[0]: " << otherA.vec[0]
                << "\nOther A int: " << otherA.num << std::endl;

        return 0;
}

运行结果

Before  A: 0x55fd59295eb0
Before  A[0]: 2
Before  A int: 1

After moving:
Orig  A: 0x55fd59295eb0
Orig  A[0]: 2
Orig  A int: 1

Other A: 0x55fd592962e0
Other A[0]: 2
Other A int: 1

问题描述

当vec为非const成员时,执行std::move后原对象访问vec会触发段错误,新对象与原对象的vec地址相同(符合常规移动语义);但当vec为const成员时,原对象的vec地址不变,访问也正常,新对象的vec是新地址。请问这种情况为何看似正常?是否属于g++下碰巧可行的未定义行为?

原因分析

  1. 默认移动构造的行为限制
    因为myclass包含const成员(num和const std::vector<int> vec),编译器生成的默认移动构造函数无法对这些const成员执行移动操作——移动语义的核心是转移资源所有权,通常会修改原对象的成员(比如把原vector的指针置空),但const成员不允许被修改。因此默认移动构造实际上退化成了逐成员的拷贝构造。

  2. 原对象未被修改的本质
    对const成员来说,默认移动构造只能复制它们的值:num直接拷贝,const std::vector也只能被拷贝(因为vector的移动构造会修改原对象内部状态,但const vector无法调用非const的移动构造函数)。这就导致原对象的所有成员都保持原样,新对象拥有一份独立的拷贝,所以原对象访问vec仍然正常,地址也不变,新对象的vec是新分配的内存(地址不同)。

  3. 未定义行为的界定
    这里原对象在移动后被访问的行为不属于未定义行为——因为默认移动构造实际上是拷贝,原对象并没有进入“可析构但不可使用”的移动后状态。但要注意:如果你的类设计初衷是使用移动语义,那么包含const成员会直接破坏移动语义,这种默认生成的“伪移动构造”可能和预期不符。

  4. 非const成员的对比
    当vec是非const时,编译器生成的默认移动构造会调用vector的移动构造函数,将原vector的资源转移给新对象,原vector会进入空状态(内部指针置空),此时访问原对象的vec[0]就会触发段错误,这是符合移动语义的正常结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 14:27:24