带模板类的通用引用

nik*_*ack 4 c++ templates universal-reference

例:

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 &&'
Run Code Online (Sandbox Code Playgroud)

通用引用似乎只适用于模板化函数,只适用于类型推导,对吧?所以在课堂上使用它没有意义吗?std::forward在我的案例中使用是否有意义?

Tar*_*ama 7

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

正如您所说,转发引用仅适用于函数模板中的类型推导.在你的情况下,当你说T&&,Tint.它不可能是int&因为它已在您的Bar实例化中明确说明.因此,参考折叠规则不会发生,因此您无法完成转发.

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

template <typename U>
void foo(U&& arg)
{
    std::forward<U>(arg); //actually do something here
}
Run Code Online (Sandbox Code Playgroud)

如果您绝对需要U具有相同的不合格类型T,您可以执行以下操作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
}
Run Code Online (Sandbox Code Playgroud)

std::decay可能对你来说有点过于激进,因为它会将数组类型衰减为指针.如果这不是你想要的,你可以编写自己的简单特征:

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>>;
Run Code Online (Sandbox Code Playgroud)

如果您需要可变版本,我们可以编写一个are_equiv特征.首先,我们需要一个特征来检查包中的所有特征是否都是真的.我会用这个bool_pack方法:

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...>;
Run Code Online (Sandbox Code Playgroud)

然后,我们需要的东西在检查每对类型Ts...Us...满足is_equiv.我们不能将两个参数包作为模板参数,所以我将使用std :: tuple来分隔它们(你可以使用一个sentinel节点,或者如果你想要的话,可以在中途拆分包):

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>...>
{};
Run Code Online (Sandbox Code Playgroud)

然后我们可以这样使用:

static_assert(are_equiv<std::tuple<Ts...>,std::tuple<Us...>>::value, 
              "Us must be equivalent to Ts");
Run Code Online (Sandbox Code Playgroud)


Que*_*tin 5

你是对的:“通用引用”仅在推导参数的类型为 时出现T&&。就您而言,没有推论(T从班级中得知),因此没有通用参考。

在您的代码片段中,将始终像常规右值引用一样std::forward执行。std::movearg

如果你想生成一个通用引用,你需要制作foo一个函数模板:

template <typename T>
class Bar
{
public:
    template <typename U>
    void foo(U&& arg)
    {
        std::forward<U>(arg);
    }
};
Run Code Online (Sandbox Code Playgroud)