接受左值引用或右值引用

Accept lvalue ref or rvalue ref

本文关键字:引用      更新时间:2023-10-16

我想写一些函数,将Object作为它们的参数之一,无论是通过左值还是右值ref都无关紧要,但绝对不是通过值,绝对只有Object。我似乎有两个选择:

void foo(Object& o) {
    // stuff
}
void foo(Object&& o) { foo(o); } // this is fine for my use-case

或者使用通用参考:

template <typename T, typename U>
using decays_to = typename std::is_same<std::decay_t<T>, U>::type;
template <typename T>
std::enable_if_t<decays_to<T, Object>::value>
foo(T&& o) 
{
    // same stuff as before
}

但第一个选项涉及编写两倍于我需要的函数,第二个选项涉及写一堆模板,这对我来说似乎有些过头了(我有点把它读成接受o任何——哦,开玩笑,真的只是一个Object)。

有没有更好的方法来解决这个问题,或者我只是被其中任何一个我感觉不太好的方法卡住了?

您的两个实现之间存在差异。第一个将允许任何可转换为ObjectObject&&Object&的内容。第二个只允许Object和以右值或左值形式继承的东西(并拒绝const Object,就像第一个一样)。

我们可以添加一个辅助对象:

template<class T>
struct l_or_r_value {
  T& t;
  l_or_r_value( T&& t_ ):t(t_) {}
  l_or_r_value( T& t_ ):t(t_) {}
  operator T&(){ return t; }
  T* operator->(){ return &t; }
  T& operator*(){ return t; }
  T& get(){ return t; }
};

然后我们可以写:

void foo(l_or_r_value<Object> o)

我们得到的行为与您的第二个解决方案非常接近,在调用点没有模板繁琐。您必须访问*oo->或执行Object& o = o_;才能获得原始引用。

它与第一个解决方案不同,因为C++不链接两个用户定义的转换。

概念提案将增加用更简洁的语法说"我接受这里的任何东西,只要它是Object"的能力。

另一种方法是只采用Object&,并使用:

tepmlate<class T>
T& lvalue( T&& t) { return t; }

在需要时将右值转换为左值(为了可爱,也可以称之为unmove

同时接受左值和右值的典型方法是生成一个使用常量引用的函数。这意味着你不能在对象上调用非常量函数,但如果你想传递右值,比如临时值,那么调用非常量的函数就没有多大意义了。