假设我有一些数据:
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 ...
你怎么做到两者兼而有之?
答案 0 :(得分:1)
struct BarIsh{
Bar&b;
operator Bar&&()&&{return std::move(b);}
operator Bar&()&&{return b;}
};
然后f(BarIsh{bar})
。
缺点是,如果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&&)>{});
}
并完成了。 (上面可能有拼写错误,手机上写的代码)