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

C++用临时对象初始化类成员时如何保证仅调用一次其构造函数

问题原因与解决方案

为什么你的代码触发了拷贝构造?

你定义的Outer构造函数中,形参inner虽然是T&&右值引用类型,但有名称的右值引用本身属于左值,你在初始化列表中直接用inner(inner)时,传入的是左值,自然匹配Inner的拷贝构造函数,不会触发移动逻辑。
如果要先改成触发移动构造,只需要修改Outer构造函数的初始化列表,对参数套std::move:

template<typename T>
struct Outer {
    T inner;
    Outer(T&& inner) : inner(std::move(inner)) {} // 加std::move
};

修改后运行输出会变成:

ctor
move ctor
dtor
dtor

已经消除了拷贝,但还是存在两次构造(一次临时对象构造,一次移动构造)。

实现仅一次Inner构造的方案

如果要完全避免额外的构造/移动操作,直接在Outer的inner成员的内存地址上构造对象,可以给Outer添加完美转发构造函数,直接把构造Inner所需的参数透传给inner的构造函数,不需要产生中间临时对象:

#include <stdio.h>
#include <utility>
#include <type_traits>

template<typename T>
struct Outer {
    T inner;

    // 完美转发构造:接收所有可用于构造T的参数,直接原地构造inner
    template<typename... Args>
    requires std::is_constructible_v<T, Args...> // 约束避免和拷贝/移动构造冲突
    Outer(Args&&... args) : inner(std::forward<Args>(args)...) {}
};

struct Inner {
    int x;
    int y;

    Inner(const int x, const int y) : x(x), y(y) { printf("ctor\n"); }
    Inner(const Inner& rhs) : x(rhs.x), y(rhs.y) { printf("copy ctor\n"); }
    Inner(Inner&& rhs) : x(std::exchange(rhs.x, 0)), y(std::exchange(rhs.y, 0)) {
        printf("move ctor\n");
    }
    Inner& operator=(const Inner& rhs) { 
        printf("assign\n"); 
        return *this = Inner(rhs); 
    }
    Inner& operator=(Inner&& rhs) { 
        printf("move assign\n");
        std::swap(x, rhs.x);
        std::swap(y, rhs.y);
        return *this;
    }
    ~Inner() { printf("dtor\n"); }
};

int main() {
    Outer<Inner> wrapper(123, 234); // 直接传构造参数,不需要生成临时Inner
    return 0;
}

运行输出如下,仅触发一次构造、一次析构,符合你的要求:

ctor
dtor

如果你一定要保留Outer<Inner> wrapper(Inner(123, 234));的传临时对象写法,在C++17及以上版本配合开启编译优化,部分编译器也可以通过强制复制消除直接把临时对象构造到Outer的inner成员位置,跳过移动构造,同样实现仅一次构造。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 10:27:05