使用 std::forward 转发重载函数
Overloading Functions with std::forward Forwarding
我想知道是否可以在转发时对类型进行一些限制,以便自动重载。例如,假设我有以下基本函数:
int f(A a, B b)
{
return g(a) + h(b);
}
其中 A
和 B
是 类 包含所有正确的复制和移动构造函数,并且 g
和 h
是每个函数有两个重载:int g(const A&)
、int g(A&&)
,h
也一样。在 f
中转发 a
和 b
的通常方法是
template <typename T1, typename T2>
int f(T1&& a, T2&& b)
{
return g(std::forward<T1>(a)) + h(std::forward<T2>(b));
}
但是,我还想添加另一种使用此功能的方法:
int f(B b, A a)
{
return g(a) + h(b);
}
如果我尝试通过此重载使用转发,生成的模板函数将如下所示:
template <typename T1, typename T2>
int f(T1&& b, T2&& a)
{
return g(std::forward<T1>(a)) + h(std::forward<T2>(b));
}
这将与第一个模板发生碰撞。
所以我的问题是,当我写一个转发的模板函数时,我是否可以约束T1
和T2
,以便在第一次重载时,T1
只能绑定到const A&
和 A&&
,而如果 T1
是 const B&
或 B&&
?
,则可以触发第二个重载
如果没有这种机制,我将需要显式地编写 8 个重载。
注意:我想我可以在这个玩具示例中使用 type_traits
中的一些模板做一些事情(有点讨厌),但我想先知道是否有更简单的方法,因为在现实中,重载可能与这个玩具示例中的不同。
can I constraint T1 and T2 so that in the first overload, T1 can only bind to const A&
and A&&
?
是的。这个概念被称为 SFINAE(替代失败不是错误),基本上看起来像这样:
template <typename T1, typename T2,
std::enable_if_t<std::is_same<A, std::decay_t<T1>>::value>* = nullptr
>
int f(T1&& a, T2&& b);
如果 T1
没有 "decay" 到 A
,那么 enable_if_t<>
类型将是错误的,并且这个重载将被抛出。
如果太冗长,你可以写一个别名:
template <typename From, typename To>
using enable_if_decays = std::enable_if_t<std::is_same<To, std::decay_t<From>>::value>;
template <typename T1, typename T2,
enable_if_decays<T1, A>* = nullptr>
int f1(T1&& a, T2&& b);
template <typename T1, typename T2,
enable_if_decays<T1, B>* = nullptr>
int f1(T1&& b, T2&& a);
// etc.
另一种可能性是根据其类型检索正确的参数
类似
template <typename T1, typename T2>
int f(T1&& t1, T2&& t2)
{
auto t = std::forward_as_tuple(t1, t2);
return g(my_get<A>(t)) + h(my_get<B>(t));
}
with my_get<T>
类似于 std::get<T>(TUPLE&)
但检索 T&&
或 T&
取决于元组内容类型。
我想知道是否可以在转发时对类型进行一些限制,以便自动重载。例如,假设我有以下基本函数:
int f(A a, B b)
{
return g(a) + h(b);
}
其中 A
和 B
是 类 包含所有正确的复制和移动构造函数,并且 g
和 h
是每个函数有两个重载:int g(const A&)
、int g(A&&)
,h
也一样。在 f
中转发 a
和 b
的通常方法是
template <typename T1, typename T2>
int f(T1&& a, T2&& b)
{
return g(std::forward<T1>(a)) + h(std::forward<T2>(b));
}
但是,我还想添加另一种使用此功能的方法:
int f(B b, A a)
{
return g(a) + h(b);
}
如果我尝试通过此重载使用转发,生成的模板函数将如下所示:
template <typename T1, typename T2>
int f(T1&& b, T2&& a)
{
return g(std::forward<T1>(a)) + h(std::forward<T2>(b));
}
这将与第一个模板发生碰撞。
所以我的问题是,当我写一个转发的模板函数时,我是否可以约束T1
和T2
,以便在第一次重载时,T1
只能绑定到const A&
和 A&&
,而如果 T1
是 const B&
或 B&&
?
如果没有这种机制,我将需要显式地编写 8 个重载。
注意:我想我可以在这个玩具示例中使用 type_traits
中的一些模板做一些事情(有点讨厌),但我想先知道是否有更简单的方法,因为在现实中,重载可能与这个玩具示例中的不同。
can I constraint T1 and T2 so that in the first overload, T1 can only bind to
const A&
andA&&
?
是的。这个概念被称为 SFINAE(替代失败不是错误),基本上看起来像这样:
template <typename T1, typename T2,
std::enable_if_t<std::is_same<A, std::decay_t<T1>>::value>* = nullptr
>
int f(T1&& a, T2&& b);
如果 T1
没有 "decay" 到 A
,那么 enable_if_t<>
类型将是错误的,并且这个重载将被抛出。
如果太冗长,你可以写一个别名:
template <typename From, typename To>
using enable_if_decays = std::enable_if_t<std::is_same<To, std::decay_t<From>>::value>;
template <typename T1, typename T2,
enable_if_decays<T1, A>* = nullptr>
int f1(T1&& a, T2&& b);
template <typename T1, typename T2,
enable_if_decays<T1, B>* = nullptr>
int f1(T1&& b, T2&& a);
// etc.
另一种可能性是根据其类型检索正确的参数
类似
template <typename T1, typename T2>
int f(T1&& t1, T2&& t2)
{
auto t = std::forward_as_tuple(t1, t2);
return g(my_get<A>(t)) + h(my_get<B>(t));
}
with my_get<T>
类似于 std::get<T>(TUPLE&)
但检索 T&&
或 T&
取决于元组内容类型。