如何在return语句中的“隐式”右值上进行类型转换

Unslander Monica:

这是无效的吗?gcc接受它,clang和msvc不接受。

#include <memory>

class Holder {
    std::unique_ptr<int> data;
public:
    operator std::unique_ptr<int>() && { return std::move(data); }
};

std::unique_ptr<int> test()
{
    Holder val;
    return val;
}

假设我不想添加std::unique_ptr<int> Holder::TakeData() { return std::move(data); },我唯一能想到的其他解决方法就是移动返回值:

std::unique_ptr<int> test()
{
    Holder val;
    return std::move(val); // lets the conversion proceed
}

但是后来gcc 9.3+告诉我那个std::move是多余的(启用了所有警告)。WTF?我的意思是,是的,gcc不需要转移,但是那时没有其他人接受该代码。如果不是gcc,那么某些人不可避免地会在以后遇到问题。

  1. 关于是否应按原样编译的权威性最后决定是什么?
  2. 这样的代码应该如何编写?我是否应该放入这个看似吵杂的TakeData功能并使用它?更糟糕的是-我是否应该使TakeData函数仅限于右值上下文,即必须这样做return std::move(val).TakeData()

添加operator std::unique_ptr<int>() & { return std::move(data); }不是一种选择,因为它显然会导致令人讨厌的错误-可以在错误的上下文中调用它。

StoryTeller-Unslander Monica:

标准要求“隐式”右值转换。但是根据您使用的是哪个标准版本,哪个“正确”的编译器会有所不同。

在C ++ 17中

[class.copy.elision](重点是我的)

3在以下复制初始化上下文中,可以使用移动操作代替复制操作:

  • 如果return语句中的表达式是一个(可能带括号的)id表达式,该对象使用在最内层的封闭函数或lambda表达式的主体或参数声明子句中声明的具有自动存储期限的对象进行命名,或者

  • ...

首先执行重载决议为副本选择构造函数,就好像该对象是由右值指定的一样。如果第一个重载解析失败或没有执行,或者所选构造函数的第一个参数的类型不是对该对象类型的右值引用(可能是cv限定的),则再次执行重载解析,将对象视为左值[注意:无论是否出现复制省略,都必须执行此两阶段重载解决方案。如果不执行省略操作,它将确定要调用的构造函数,并且即使取消了调用,所选的构造函数也必须可访问。—尾注]

在C ++ 17之前,GCC是错误的。使用val隐式的右值应该初始化失败是考虑粗体字标识句话我的返回类型(在右值参考unique_ptrc'tor不绑定直接val)。但是来到C ++ 20时,该句子不再存在。

C ++ 20

3隐式可移动实体是自动存储持续时间的变量,它是非易失性对象或对非易失性对象类型的右值引用。在以下复制初始化上下文中,可以使用移动操作代替复制操作:

  • 如果return([stmt.return])或co_return([stmt.return.coroutine])语句中的表达式是一个(可能带括号的)id表达式,该表达式命名了在主体或参数声明子句中声明的隐式可移动实体最内层的封闭函数或lambda表达式,或

  • [...]

选择要复制的构造函数或return_value的重载解析首先要执行,就好像表达式或操作数是一个右值一样。如果第一个重载解析失败或没有执行,则将表达式或操作数视为左值,再次执行重载解析。[注意:无论是否出现复制省略,都必须执行此两阶段重载解决方案。如果不执行省略操作,它将确定要调用的构造函数或return_value重载,并且即使取消了调用,所选的构造函数或return_value重载也必须可访问。—尾注]

因此,代码的正确性取决于编译器的时间旅行属性。

至于应该如何编写这样的代码。如果无法获得一致的结果,则可以选择使用函数的确切返回类型

std::unique_ptr<int> test()
{
    Holder val;
    std::unique_ptr<int> ret_val = std::move(val);
    return ret_val;
}

我从一开始就同意,这可能看起来不像简单地返回就那么吸引人val,但至少在NRVO中它表现得很好。因此,我们获得的副本的可能性不会unique_ptr超过我们最初想要的副本

如果这仍然不太吸引人,那么我发现您最喜欢的是窃取成员函数的想法。但没有占味。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何禁用常量的隐式类型转换?

如何在CALayer上进行转换?

如何在laravel的if语句中显示值

值如何在foreach语句中打印?

using语句中使用的类型必须隐式转换为'System.IDisposable'

允许对单个值进行隐式转换

'System.Security.Cryptography.Rfc2898DeriveBytes':using语句中使用的类型必须隐式转换为System.IDisposable

如何在Scala中隐式函数类型可以对效果进行建模?

C ++如何在不进行隐式转换的情况下模拟函数?

什么时候在C ++中进行隐式类型转换?

如何在return语句中编写循环?

如果语句不能隐式将类型转换为bool?

如何在Scala中正确使用歧义隐式进行类型求反

返回值的隐式类型转换

如何在switch语句中缩小类型

return语句中使用的局部变量不会隐式转换为r值以匹配转换运算符

如何在函数参数中强制执行类型并避免隐式转换?

如何在成员函数中使用隐式类型转换?

C ++如何在调用函数时阻止编译器进行隐式转换?

Linq.Expressions值类型隐式转换

C ++如何在右值引用上进行模板推导?

严格的选项不允许在“布尔值”之间进行隐式转换 在SQL语句中布尔

无法将类型'string'隐式转换为'bool'[If语句]

无法隐式转换类型

Fortran if语句中的隐式转换整数<->逻辑

在 using 语句中处理隐式引用

eRuby - 在 if 语句中比较数字时没有将 nil 隐式转换为 String

“IEnumerator”:在 using 语句中使用的类型必须可隐式转换为“System.IDisposable”

如何在 TypeScript 中将数组类型隐式转换为元组类型?