如何存储通用引用 [英] How to store universal references

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

问题描述

我需要在类中存储通用引用(我确信引用的值将超过类)。有没有一种规范的方式这样做?

I need to store universal references inside a class (I am sure the referenced values will outlive the class). Is there a canonical way of doing so?

这是一个最小的例子,我想出了。它似乎工作,但我不知道如果我是正确的。

Here is a minimal example of what I have come up with. It seems to work, but I'm not sure if I got it right.

template <typename F, typename X>
struct binder
{
    template <typename G, typename Y>
    binder(G&& g, Y&& y) : f(std::forward<G>(g)), x(std::forward<Y>(y)) {}

    void operator()() { f(std::forward<X>(x)); }

    F&& f;
    X&& x;
};

template <typename F, typename X>
binder<F&&, X&&> bind(F&& f, X&& x)
{
    return binder<F&&, X&&>(std::forward<F>(f), std::forward<X>(x));
}

void test()
{
    int i = 1;
    const int j = 2;
    auto f = [](int){};

    bind(f, i)();   // X&& is int&
    bind(f, j)();   // X&& is const int&
    bind(f, 3)();   // X&& is int&&
}

我的推理是正确的还是会导致微妙的错误?此外,有没有更好的(即更简洁)的方式来编写构造函数? binder(F& f,X&& x)将不起作用,因为那些是r值引用,因此不允许 binder ,i)

Is my reasoning correct or will this lead to subtle bugs? Also, is there a better (i.e., more concise) way to write the constructor? binder(F&& f, X&& x) will not work, since those are r-value references, thus disallowing binder(f, i).

推荐答案

您不能存储通用引用,因为没有事情,只有右值引用和左值引用。 通用引用是Scott Meyers描述语法特征的方便术语,但它不是类型系统的一部分

You can't "store a universal reference" because there's no such thing, there are only rvalue references and lvalue references. "Universal reference" is Scott Meyers's convenient term to describe a syntax feature, but it's not part of the type system.

代码的细节:

template <typename F, typename X>
binder<F&&, X&&> bind(F&& f, X&& x)

c $ c> binder 以引用类型作为模板参数,因此在类定义中不需要将成员声明为rvalue-references,因为它们已经引用类型(由 bind 推导出的左值或右值)。这意味着您总是有比需要更多的&& 令牌,这是多余的,并由于参考崩溃而消失。

Here you're instantiating binder with reference types as the template arguments, so in the class definition there is no need to declare the members as rvalue-references, because they already are reference types (either lvalue or rvalue as deduced by bind). This means you've always got more && tokens than needed, which are redundant and disappear due to reference collapsing.

如果您确定 binder 将始终由您的 bind 函数实例化(因此始终实例化参考类型),那么你可以这样定义:

If you're sure binder will always be instantiated by your bind function (and so always be instantiated with reference types) then you could define it like this:

template <typename F, typename X>
struct binder
{
    binder(F g, X y) : f(std::forward<F>(g)), x(std::forward<X>(y)) {}

    void operator()() { f(std::forward<X>(x)); }

    F f;
    X x;
};

在此版本中, F code> X 是引用类型,因此使用 F&&& X& ,因为它们已经是左值引用(因此&& 什么也不做),或者它们是右值引用$ c>&&& 在此情况下也不会执行任何操作。)

In this version the types F and X are reference types, so it's redundant to use F&& and X&& because they're either already lvalue references (so the && does nothing) or they're rvalue references (so the && does nothing in this case too!)

/ code>,并将 bind 更改为:

Alternatively, you could keep binder as you have it and change bind to:

template <typename F, typename X>
binder<F, X> bind(F&& f, X&& x)
{
    return binder<F, X>(std::forward<F>(f), std::forward<X>(x));
}

现在你实例化 binder 使用一个左值引用类型或一个对象(即非引用)类型,然后在 binder 中声明具有附加& 所以它们是左值引用类型或右值引用类型。

Now you instantiate binder with either an lvalue reference type or an object (i.e. non-reference) type, then inside binder you declare members with the additional && so they are either lvalue reference types or rvalue reference types.

此外,如果你考虑它,你不需要存储rvalue参考成员。通过左值引用存储对象是非常好的,所有重要的是,您可以将它们正确转换为运算符()函数中的左值或右值。所以类成员可以只是 F& X& (或者在你总是实例化类型参考参数, F X

Furthermore, if you think about it, you don't need to store rvalue reference members. It's perfectly fine to store the objects by lvalue references, all that matters is that you forward them correctly as lvalues or rvalues in the operator() function. So the class members could be just F& and X& (or in the case where you always instantiate the type with reference arguments anyway, F and X)

我将简化代码为:

template <typename F, typename X>
struct binder
{
    binder(F& g, X& y) : f(g), x(y) { }

    void operator()() { f(std::forward<X>(x)); }

    F& f;
    X& x;
};

template <typename F, typename X>
binder<F, X> bind(F&& f, X&& x)
{
    return binder<F, X>(f, x);
}

此版本保留模板参数中所需的类型 F X ,并在 std :: forward< X>(x)表达式,这是唯一需要它的地方。

This version preserves the desired type in the template parameters F and X and uses the right type in the std::forward<X>(x) expression, which is the only place where it's needed.

最后,我发现它更准确,更有助于推断类型, (折叠)引用类型:

Finally, I find it more accurate and more helpful to think in terms of the deduced type, not just the (collapsed) reference type:

bind(f, i)();   // X is int&, X&& is int&
bind(f, j)();   // X is const int&, X&& is const int&
bind(f, 3)();   // X is int, X&& is int&&

这篇关于如何存储通用引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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