接受左值引用或右值引用

3

我想编写一些函数,其中一个参数是Object,无论是通过左值还是右值引用传递都可以,但绝对不能通过值传递,并且只能是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)。

是否有更好的方法来解决这个问题,或者我只是在两种方案中选择较少烦恼的那个?


3
void foo(Object const& o) 足够了吗?它可以接受 r-value 和 l-value,但是是 const 的。 - Niall
1
@Niall 哇,这确实可能会发生。有时候我会过度思考事情。 - Barry
std::remove_const_t<std::remove_reference_t<T>>std::decay_t<T>更好,因为decay_t会产生函数指针和数组指针转换的副作用。 - Casey
2个回答

6

你的两个实现有所区别。第一个将接受任何可以转换为 ObjectObject&&Object& 的东西。第二个仅在 rvalue 或 lvalue 形式下接受 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)

我们采用与第二种解决方案非常接近的行为,但在调用点没有模板 mumbo jumbo。 您必须访问 *oo-> 或执行 Object& o = o_; 来访问原始引用。

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

概念提案将添加能力,以更简洁的语法说“我取这里的任何东西,只要它是一个 Object”。

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

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

当你需要时将rvalues转换为lvalues(你也可以称之为unmove


我真的很喜欢这个答案,并且可能会在某处使用 l_or_r_value,但是我接受另一个答案,因为它对我的使用情况更加简单明了。 - Barry
@Barry 啊!抱歉,我以为你实际上想要修改函数中的值,并且拒绝 const 对象是有意的。 - Yakk - Adam Nevraumont
1
不,仅仅是因为键盘和椅子之间存在的问题而拒绝它们。 - Barry

4

接受左值和右值的典型方法是创建一个使用const引用的函数。这意味着您不能在对象上调用非const函数,但是如果要传递临时变量等右值,那么调用非const函数也并不一定有意义。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接