根据函数签名将引用作为左值/右值传递

Passing reference as lvalue/rvalue depending on function signature

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

假设我有一些数据:

struct Bar {};

我需要包装一个函数,并用这些数据提供给它。

template<typename F>
void foo(F f) {
    Bar bar;
    f(bar);
}

正如您在这个琐碎的例子中看到的:

  • bar不是临时的
  • 我打电话给f后就不需要了

我想支持多功能签名,例如:

foo([](Bar){}); // (1)
foo([](Bar&){}); // (2)
foo([](Bar&&){}); // (3)

然而,gcc抱怨道:

f(bar); // (3) : cannot bind 'Bar' lvalue to 'Bar&&'
f(std::move(bar)); // (2) : no match for call to ...

你会怎么做到这两个?

struct BarIsh{
  Bar&b;
  operator Bar&&()&&{return std::move(b);}
  operator Bar&()&&{return b;}
};

然后是CCD_ 3。

缺点是,如果f取一个推导的参数,它得到的是BarIsh,而不是Bar

假设你有一个SFINAE友好的result_of。。。

template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<class...>struct types{using type=types;};
namespace details{
  template<template<class...>class Z,class types,class=void>
  struct can_apply:std::false_type{};
  template<template<class...>class Z,class...Ts>
  struct can_apply<Z,types<Ts...>,void_t<Z<Ts...>>>:
    std::true_type
  {};
};
template<template<class...>class Z,class...Ts>
using can_apply=details::can_apply<Z,types<Ts...>>;
template<class Sig>
using result_of_t=typename std::result_of<Sig>::type;
template<class Sig>
using can_invoke=can_apply<result_of_t,Sig>;

现在我们可以测试了。

template<typename F>
void foo(F&& f,std::true_type)
{
  Bar bar;
  std::forward<F>(f)(std::move(bar));
}
template<typename F>
void foo(F&& f,std::false_type)
{
  Bar bar;
  std::forward<F>(f)(bar);
}

template<typename F>
void foo(F f)
{
  foo(std::forward<F>(f),can_apply<F(Bar&&)>{});
}

并完成。(上面可能有打字错误,代码写在手机上)