问题描述
示例:
template 班级酒吧{民众:void foo(T&& arg){std::forward(arg);}};Bar酒吧;bar.foo(10);//有效诠释{10};bar.foo(a);//错误 C2664:无法将参数 1 从int"转换为int &&"
似乎通用引用仅适用于模板化函数,而仅适用于类型推导,对吗?那么在课堂上使用它没有意义吗?在我的情况下使用 std::forward
有意义吗?
请注意,首选术语(即规范的未来版本中将使用的术语)现在是转发引用.>
如您所说,转发引用仅适用于函数模板中的类型推导.就您而言,当您说 T&&
时,T
是 int
.它不能是 int&
,因为它已在您的 Bar
实例化中明确说明.因此,引用折叠规则不会发生,因此您无法进行完美转发.
如果你想在这样的成员函数中做完美转发,你需要有一个成员函数模板:
template void foo(U&& arg){std::forward(arg);//实际上在这里做点什么}
如果您绝对需要 U
与 T
具有相同的非限定类型,则可以执行 static_assert
:
template void foo(U&& arg){static_assert(std::is_same<std::decay_t<U>,std::decay_t<T>>::value,"U 必须与 T 相同");std::forward(arg);//实际上在这里做点什么}
std::decay
对您来说可能有点过于激进,因为它会将数组类型衰减为指针.如果这不是您想要的,您可以编写自己的简单特征:
template 使用 remove_cv_ref = std::remove_cv_t;模板 <typename T, typename U>使用 is_equiv = std::is_same<remove_cv_ref<T>, remove_cv_ref<U>;
如果您需要可变参数版本,我们可以编写一个 are_equiv
trait.首先,我们需要一个特征来检查包中的所有特征是否都为真.我将使用 bool_pack
方法:
命名空间细节{模板struct bool_pack;模板使用 all_true = std::is_same, bool_pack;}模板 <typename... Ts>使用 all_true = detail::all_true;
然后我们需要检查Ts...
和Us...
中的每一对类型是否满足is_equiv
.我们不能将两个参数包作为模板参数,所以我将使用 std::tuple 来分隔它们(您可以使用哨兵节点,或者如果您愿意,可以将包中途拆分):
template 结构 are_equiv;template struct are_equiv <std::tuple<Ts...>, std::tuple<Us...>: all_true{};
然后我们可以这样使用:
static_assert(are_equiv<std::tuple<Ts...>,std::tuple<Us...>>::value,"我们必须等同于 Ts");
Example:
template <typename T>
class Bar
{
public:
void foo(T&& arg)
{
std::forward<T>(arg);
}
};
Bar<int> bar;
bar.foo(10); // works
int a{ 10 };
bar.foo(a); // error C2664: cannot convert argument 1 from 'int' to 'int &&'
It seems that universal references works only with templated functions and only with type deduction, right? So it make no sense to use it with class? And does using of std::forward
makes sense in my case?
Note that the preferred terminology (i.e. the one which will be in future versions of the spec) is now forwarding reference.
As you say, a forwarding reference only works with type deduction in a function template. In your case, when you say T&&
, T
is int
. It can't be int&
because it has been explicitly stated in your Bar
instantiation. As such, reference-collapsing rules can't occur, so you can't do perfect forwarding.
If you want to do perfect forwarding in a member function like that, you need to have a member function template:
template <typename U>
void foo(U&& arg)
{
std::forward<U>(arg); //actually do something here
}
If you absolutely need U
to have the same unqualified type as T
, you can do a static_assert
:
template <typename U>
void foo(U&& arg)
{
static_assert(std::is_same<std::decay_t<U>,std::decay_t<T>>::value,
"U must be the same as T");
std::forward<U>(arg); //actually do something here
}
std::decay
might be a bit too aggressive for you as it will decay array types to pointers. If that's not what you want, you could write your own simple trait:
template <typename T>
using remove_cv_ref = std::remove_cv_t<std::remove_reference_t<T>>;
template <typename T, typename U>
using is_equiv = std::is_same<remove_cv_ref<T>, remove_cv_ref<U>>;
If you need a variadic version, we can write an are_equiv
trait. First we need a trait to check if all traits in a pack are true. I'll use the bool_pack
method:
namespace detail
{
template<bool...> struct bool_pack;
template<bool... bs>
using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;
}
template <typename... Ts>
using all_true = detail::all_true<Ts::value...>;
Then we need something to check if each pair of types in Ts...
and Us...
satisfy is_equiv
. We can't take two parameter packs as template arguments, so I'll use std::tuple to separate them (you could use a sentinel node, or split the pack halfway through instead if you wanted):
template <typename TTuple, typename UTuple>
struct are_equiv;
template <typename... Ts, typename... Us>
struct are_equiv <std::tuple<Ts...>, std::tuple<Us...>> : all_true<is_equiv<Ts,Us>...>
{};
Then we can use this like:
static_assert(are_equiv<std::tuple<Ts...>,std::tuple<Us...>>::value,
"Us must be equivalent to Ts");
这篇关于模板化类的通用参考的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!