C++20返回语句隐式移动规则的编译器兼容与类型适配问题
C++20隐式移动规则的编译器行为分析
C++20中有一条非正式称为“返回变量时优先尝试移动,再尝试复制”的规则,其正式定义对应标准中的**[class.copy.elision]/3**条款。以下是测试用的函数模板:
template<typename T> T f() { struct { T x; operator T&&() && { return static_cast<T&&>(x); } } s; return s; }
该模板返回自动存储期变量s,且包含一个接受右值引用的转换运算符,用于构造返回值T。预期该模板对任意类型T都能生效,但实际测试得到以下结果:
- 当
T为类类型A时,GCC和Clang编译通过,但MSVC报错; - 当
T为标量类型int时,所有编译器均报错。
疑问解答
1. MSVC此处是否存在bug?
是,MSVC存在实现缺陷。根据**[class.copy.elision]/3**的规则:当返回语句的表达式是自动存储期对象(非函数参数)时,编译器会先尝试复制消除(直接将返回值初始化到该对象的存储位置);若无法消除,则优先把该对象当作右值构造返回值——这包括通过用户定义的右值转换运算符来生成符合要求的右值。
对于类类型T,GCC和Clang正确触发了s的右值转换运算符,生成T&&后完成返回值初始化;但MSVC未正确处理这种通过用户定义转换生成右值的场景,不符合标准要求,属于编译器bug。
2. 该隐式移动规则仅适用于类类型而不适用于标量类型是否符合标准预期?
符合标准预期。[class.copy.elision]/3的隐式移动规则本质是针对类类型对象的复制/移动优化逻辑,标量类型不存在独立的移动语义——标量的“移动”和复制完全等价,标准也未对标量类型定义隐式移动规则。
当T是int这类标量时,return s;需要将s转换为int。虽然s有转换为int&&的运算符,但标量类型的返回值初始化不支持通过这种右值引用转换完成:标量初始化只能直接从同类型或可直接转换的值进行,隐式移动规则不对标量生效,因此所有编译器报错是符合标准的行为。
内容的提问来源于stack exchange,提问作者Fedor
相关产品推荐
相关产品推荐

