首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何返回转发引用?

如何返回转发引用?
EN

Stack Overflow用户
提问于 2018-09-14 04:57:08
回答 1查看 348关注 0票数 2

:什么是返回前向(通用)引用的最自然的方式?

代码语言:javascript
运行
复制
struct B{
         template<class Ar> friend
/*   1*/ Ar&& operator<<(Ar&& ar, A const& a){...; return std::forward<Archive>(ar);}
         template<class Ar> friend 
/*OR 2*/ Ar& operator<<(Ar&& ar, A const& a){...; return ar;}
         template<class Ar> friend 
/*OR 3*/ Ar  operator<<(Ar&& ar, A const& a){...; return std::forward<Ar>(ar);}
         template<class Ar> friend 
/*OR 4*/ Ar  operator<<(Ar&& ar, A const& a){...; return ar;}
/*OR 5*/ other??
}

我想到的情况是一个类似流的对象,它被构造并立即使用。例如:

代码语言:javascript
运行
复制
dosomethign( myarchive_type{} << B{} << B{} << other_unrelated_type{} );
EN

回答 1

Stack Overflow用户

发布于 2018-09-14 14:08:21

返回rvalue引用很少是个好主意。罕见的例外情况是,您正在编写逻辑上等效于std::forward的函数。

它之所以是个坏主意,是因为引用生存期扩展不是传递性的,而且您很容易不小心得到挂起的引用。

代码语言:javascript
运行
复制
const auto& foo = some_operation;

如果some_operation是prvalue表达式,则临时的生存期将扩展到foo的生存期。但是,如果some_operation是:

代码语言:javascript
运行
复制
foo( some_prvalue_expression )

foo接受一个rvalue引用并返回它时,您将得到一个悬空引用。

对于移动成本低的类型,解决方案很简单:

代码语言:javascript
运行
复制
struct B{
  template<class Ar> friend
  Ar operator<<(Ar&& ar, A const& a){...; return std::forward<Archive>(ar);}
}

如果传递一个lvalue,则通过引用返回,如果传递一个rvalue,则返回值。

现在,引用生存期扩展正常工作。

这要求你的类型是便宜的移动;所以

代码语言:javascript
运行
复制
myarchive_type{} << B{} << B{} << other_unrelated_type{};

结果myarchive_type{}被移动3次。

如果您的类型移动起来不便宜,可以考虑简单地阻止rvalue版本;缺乏安全性使其不值得。

对于一个具体的例子,请看一下这个函数:

代码语言:javascript
运行
复制
template<class C>
struct backwards {
  C c;
  auto begin() const {
    using std::rbegin;
    return rbegin(c);
  }
  auto end() const {
    using std::rend;
    return rend(c);
  }
};
template<class C>
backwards(C&&) -> backwards<C>;

(如果我把扣减指南搞错了,很抱歉)。

现在我们这样做:

代码语言:javascript
运行
复制
for( auto x : backwards{ std::vector<int>{1,2,3} } ) {
  std::cout << x << ",";
}

vector被移动到backwards中,但是如果我们这样做:

代码语言:javascript
运行
复制
auto vec = std::vector<int>{1,2,3};
for( auto x : backwards{ vec } ) {
  std::cout << x << ",";
}

不复制。

如果没有“创建副本并在传递rvalue的情况下返回它”,上面的第一个for循环将有一个悬空引用。

我看到了一些建议或建议的想法,它们试图概括引用生存期扩展,或者至少向编译器提供提示,说明函数的返回值取决于传递给它的参数的生存期,以便在以这种方式使用悬空引用时,它可以生成错误/警告。据我所知,没有一家公司把它变成了C++标准。

在不这样做之前,随意返回一个参考价值对我的血液来说太危险了。

假设你考虑搬家很贵。好吧,这是可行的:

代码语言:javascript
运行
复制
std::invoke([&](auto&& ar){
  dosomething( ar << << B{} << B{} << other_unrelated_type{} );
}, myarchive_type{} );

它接受临时的myarchive_type{}并将其存储在一个rvalue引用中。

然后它把它传递给一只羔羊。在lambda内部,名称ar是一个lvalue。我们继续在兰博达内安全使用它。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52325198

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档