结构体内部对象不同初始化方式的差异原因咨询
我定义了如下类:
// NetworkManager.hpp class NetworkManager : public QObject { Q_OBJECT public: NetworkManager(QHostAddress const& address, quint16 port); private: QHostAddress const& _address; quint16 const _port; }; // NetworkManager.cpp NetworkManager(QHostAddress const& address, quint16 const port) : _address(address) , _port(port) { }
该类被包含在如下结构体中:
struct Context { NetworkManager networkManager; };
我尝试两种方式初始化该结构体:使用_context({{QHostAddress::SpecialAddress::LocalHost, 1337}})时,会调用因继承QObject而被默认删除的NetworkManager拷贝构造函数;但使用_context{{QHostAddress::SpecialAddress::LocalHost, 1337}}却能正常工作。我使用Clang编译器与C++20标准,请问这是为什么?
这两种初始化方式的核心差异在于直接初始化和聚合初始化的规则区别,结合C++20特性与QObject的不可拷贝限制,导致了不同结果:
括号初始化
_context({{...}})的行为
用圆括号包裹初始化器时,编译器会触发直接初始化逻辑:外层(...)会尝试先创建一个临时的NetworkManager对象,再通过拷贝构造函数将这个临时对象赋值给Context的networkManager成员。但NetworkManager继承自QObject,而QObject的拷贝构造函数是被默认删除的(由Q_DISABLE_COPY宏实现),因此这一步会触发编译错误。花括号初始化
_context{{...}}的行为
用双层花括号{{...}}时,编译器执行的是聚合初始化:C++20标准允许对无自定义构造函数的聚合体(Context属于这类)的成员直接初始化,外层花括号对应Context,内层花括号直接传递给NetworkManager的双参数构造函数,直接在Context的内存空间中构造NetworkManager对象,全程不需要拷贝或移动操作,自然不会触发QObject不可拷贝的限制,因此能正常编译。
另外需要提醒:你的NetworkManager中持有QHostAddress const&成员变量,存在悬空引用风险——如果构造时传入的是临时QHostAddress对象,当临时对象生命周期结束后,_address会变成无效引用,建议改为存储值而非引用。
内容的提问来源于stack exchange,提问作者Mark

