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

MSVC与GCC/Clang临时生命周期变量行为差异问询

GCC/Clang与MSVC下C++代码的行为差异解析

问题代码与现象

先看这段C++代码:

#include <iostream>

class Base
{
public:
    Base(int* a) : ptr{a} { std::cout << "Base construct\n"; }
    ~Base() { std::cout << "Base destruct\n"; }

    int* ptr;
};

class Derived : public Base
{
public:
    Derived() : Base(&val) { std::cout << "Derived construct\n"; }

    ~Derived() { std::cout << "Derived destruct\n"; }
protected:
    int val{2};
};

Derived GiveMeDerived() {
    return {};
}

void SomeFunc(Base b) {
    std::cout << "SomeFunc " << *b.ptr << "\n";
}

int main()
{
    SomeFunc(GiveMeDerived());

    return 0;
}

不同编译器输出差异

  • GCC/Clang输出:
Base construct
Derived construct
SomeFunc 2
Base destruct
Derived destruct
Base destruct
  • MSVC 2022(版本19.39.33523.0)输出:
Base construct
Derived construct
SomeFunc 2
Base destruct
Base destruct
Derived destruct
Base destruct

MSVC多了一次Base destruct调用,这是核心疑问点。

原因分析

这个额外的Base析构,本质是MSVC在特定优化场景下的临时对象生成行为:

  1. 当GiveMeDerived()返回临时Derived对象,传递给接受Base值参数的SomeFunc时,需要进行切片操作(将Derived对象切片为Base对象)。
  2. GCC/Clang中,切片操作直接在临时Derived对象上完成,生成的Base参数对象析构对应一次Base destruct;而原代码中Base只有指针成员,属于平凡类型,MSVC的优化逻辑可能额外生成了一个临时Base对象,导致多一次析构。
  3. 原代码中Base和Derived都使用编译器生成的默认拷贝构造,当类是平凡类型时,MSVC的拷贝/切片逻辑可能产生额外临时副本。

验证案例:添加追踪后的行为

当修改代码,给Base添加非平凡成员(std::string)并添加追踪逻辑后:

#include <iostream>

class Base
{
public:
    Base(int* a) : ptr{a} { std::cout << "Base construct\n"; }
    ~Base() { std::cout << "Base destruct " << default_des << "\n"; }

    int* ptr;
    std::string default_des{"default"};
};

class Derived : public Base
{
public:
    Derived() : Base(&val) { std::cout << "Derived construct\n"; }

    ~Derived() { std::cout << "Derived destruct\n"; }
protected:
    int val{2};
};

Derived GiveMeDerived() {
    Derived a;
    a.default_des = "From GiveMeDerived\n";
    return a;
}

void SomeFunc(Base b) {
    std::cout << "SomeFunc " << *b.ptr << "\n";
    b.default_des = "inside SomeFunc";
}

int main()
{
    SomeFunc(GiveMeDerived());

    return 0;
}

此时MSVC输出恢复正常:

Base construct
Derived construct
SomeFunc 2
Base destruct inside SomeFunc
Derived destruct
Base destruct From GiveMeDerived

这是因为添加std::string后,Base的默认拷贝构造变为非平凡拷贝构造,MSVC的优化逻辑不再生成额外临时对象,切片操作完全按标准流程执行,析构次数与GCC/Clang一致。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 22:42:42