C++ 如何在“上进行类型转换”;隐式;return语句中的rvalue

C++ 如何在“上进行类型转换”;隐式;return语句中的rvalue,c++,language-lawyer,C++,Language Lawyer,这是无效的吗?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;

这是无效的吗?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::唯一的ptr数据;
公众:
运算符std::unique_ptr()&&{return std::move(data);}
};
标准::唯一性测试()
{
持有者val;
返回val;
}
假设我不想添加类似
std::unique_ptr Holder::TakeData(){return std::move(data);}
,我唯一能想到的其他解决方法是在返回值中移动:

std::unique_ptr<int> test()
{
    Holder val;
    return std::move(val); // lets the conversion proceed
}
std::unique_ptr test()
{
持有者val;
return std::move(val);//让转换继续进行
}
但是gcc 9.3+却厚颜无耻地告诉我,
std::move
是冗余的(启用了所有警告)。世界跆拳道联盟?我的意思是,是的,
gcc
不需要移动,当然,但是其他任何东西都不接受代码。如果不是gcc,那么一些人以后不可避免地会对此犹豫不决

  • 关于它是否应该按原样编译,权威的结论是什么
  • 这样的代码应该如何编写?我应该加入这个看似嘈杂的函数并使用它吗?更糟糕的是-我是否应该将
    TakeData
    函数限制为右值上下文,即必须执行
    返回std::move(val).TakeData()
  • 添加
    运算符std::unique_ptr()&{return std::move(data);}
    不是一个选项,因为它显然会导致严重的错误-它可以在错误的上下文中调用。

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

    在C++17中

    [class.copy.elision](强调我的)

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

    • 如果return语句中的表达式是用automatic命名对象的id表达式(可能带括号) 在body或parameter声明子句中声明的存储持续时间 最里面的封闭函数或lambda表达式,或

    选择副本构造函数的重载解析是第一个 像对象由右值指定一样执行。如果第一个 过载解析失败或未执行,或者如果 所选构造函数的第一个参数不是右值引用 对于对象的类型(可能是cv限定),重载解析为 再次执行,将对象视为左值。[ 注:此 无论是否 将发生复制省略。它确定在以下情况下调用的构造函数: 不执行省略,并且所选构造函数必须为 即使电话被省略,也可以访问。 — 尾注 ]

    在C++17之前,GCC是错误的。隐式使用
    val
    作为右值应该无法初始化返回类型,因为我用粗体标记了一个句子(在
    unique\u ptr
    C'tor中的右值引用不直接绑定到
    val
    ),但在C++20中,这个句子就不存在了

    C++20

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

    • 如果return([stmt.return])或co­return([stmt.return.coroutine])语句中的表达式是(可能带括号) id表达式,该表达式命名在 最里面的封闭表达式的body或parameter声明子句 函数或lambda表达式,或

    • [……]

    重载解析以选择副本的构造函数或 首先执行返回值重载调用,就像表达式 或操作数是右值。如果第一个重载解析失败或 未执行,再次执行过载解决,考虑到 作为左值的表达式或操作数。[注:这是两个阶段] 无论是否复制,都必须执行重载解析 将发生省略。它确定构造函数或返回值 如果未执行省略,则调用重载,并选择 即使 通话被省略了。-结束语]

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

    如果你不能得到一致的结果,一个选择就是使用函数的确切返回类型

    std::unique_ptr<int> test()
    {
        Holder val;
        std::unique_ptr<int> ret_val = std::move(val);
        return ret_val;
    }
    
    std::unique_ptr test()
    {
    持有者val;
    std::unique_ptr ret_val=std::move(val);
    返回返回值;
    }
    
    我从一开始就同意,这看起来可能不像简单地返回
    val
    那么吸引人,但至少它在NRVO中起到了很好的作用。因此,我们不太可能获得比最初期望的更多的
    unique\u ptr

    如果这仍然太不吸引人,那么我觉得你关于窃取资源的成员函数的想法最符合我的喜好。但是没有考虑品味。

    我认为答案是肯定的,但措辞在我看来毫无意义。