模板化类的通用参考 [英] Universal reference with templated class

查看:21
本文介绍了模板化类的通用参考的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

示例:

template 班级酒吧{民众:void foo(T&& arg){std::forward(arg);}};Bar酒吧;bar.foo(10);//有效诠释{10};bar.foo(a);//错误 C2664:无法将参数 1 从int"转换为int &&"

似乎通用引用适用于模板化函数,而适用于类型推导,对吗?那么在课堂上使用它没有意义吗?在我的情况下使用 std::forward 有意义吗?

解决方案

请注意,首选术语(即规范的未来版本中将使用的术语)现在是转发引用.>

如您所说,转发引用仅适用于函数模板中的类型推导.就您而言,当您说 T&& 时,Tint.它不能是 int&,因为它已在您的 Bar 实例化中明确说明.因此,引用折叠规则不会发生,因此您无法进行完美转发.

如果你想在这样的成员函数中做完美转发,你需要有一个成员函数模板:

template void foo(U&& arg){std::forward(arg);//实际上在这里做点什么}

如果您绝对需要 UT 具有相同的非限定类型,则可以执行 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");

这篇关于模板化类的通用参考的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆