模板代码中的转发引用与const左值引用 [英] Forwarding reference vs const lvalue reference in template code

查看:83
本文介绍了模板代码中的转发引用与const左值引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近一直在研究C ++中的转发引用,以下是我对该概念的当前理解的简要总结。

I've recently been looking into forwarding references in C++ and below is a quick summary of my current understanding of the concept.

假设我具有模板功能 foo 对类型 T 的单个参数进行转发引用。

Let's say I have a template function footaking a forwarding reference to a single argument of type T.

template<typename T>
void foo(T&& arg);

如果我使用左值调用此函数,则 T 将推导为 T& ,从而使 arg 参数的类型为 T& ,因为参考折叠规则 T& && -> T&

If I call this function with an lvalue then T will be deduced as T& making the arg parameter be of type T& due to the reference collapsing rules T& && -> T&.

如果使用未命名的临时函数(例如函数调用的结果)调用此函数,则 T 将推导出为 T ,从而使 arg 参数的类型为 T&

If this function gets called with an unnamed temporary, such as the result of a function call, then Twill be deduced as T making the arg parameter be of type T&&.

foo 内部,但 arg 是一个命名参数,因此如果要将参数传递给其他函数,则需要使用 std :: forward 仍然保持其值类别。

Inside foo however, arg is a named parameter so I will need to use std::forward if I want to pass the parameter along to other functions and still maintain its value category.

template<typename T>
void foo(T&& arg)
{
    bar(std::forward<T>(arg));
}

据我了解,cv限定词不受此转发的影响。这意味着如果我使用命名的const变量调用foo,则 T 将被推导出为 const T& ,因此由于引用折叠规则, arg 的类型也将为 const T& 。对于常量值, T 将推导为 const T ,因此 arg 的类型为 const T&&

As far as I understand the cv-qualifiers are unaffected by this forwarding. This means that if I call foo with a named const variable then T will be deduced as const T& and hence the type of arg will also be const T& due to the reference collapsing rules. For const rvalues T will be deduced as const T and hence arg will be of type const T&&.

这也意味着如果我修改了在 foo 内的 arg 如果确实向其传递const变量,则会收到编译时错误。

This also means that if I modify the value of arg inside foo I will get a compile time error if I did infact pass a const variable to it.

现在进入我的问题。
假设我正在编写一个容器类,并想提供一种将对象插入到我的容器中的方法。

Now onto my question. Assume I am writing a container class and want to provide a method for inserting objects into my container.

template<typename T>
class Container
{
public:
    void insert(T&& obj) { storage[size++] = std::forward<T>(obj); }
private:
    T *storage;
    std::size_t size;
    /* ... */
};

通过将 insert 成员函数设为转发对 obj 的引用我可以使用 std :: forward 来利用存储类型<的移动赋值运算符 T 如果插入实际上是通过临时对象。

By making the insert member function take a forwarding reference to obj I can use std::forward to take advantage of the move assignment operator of the stored type T if insert was infact passed a temporary object.

以前,当我对转发引用一无所知时,我会使用const左值引用编写此成员函数:
void insert(const T& obj)

Previously, when I didn't know anything about forwarding references I would have written this member function taking a const lvalue reference: void insert(const T& obj).

其缺点是,如果插入被传递了一个临时对象。

The downside of this is that this code does not take advantage of the (presumably more efficient) move assignment operator if insert was passed a temporary object.

假设我什么都没错过。

是否有必要为insert函数提供两个重载?

void insert(const T& obj);
void insert(T&& obj);

我要问的原因是 std :: vector 的参考文档指出 push_back 方法有两个重载。

The reason I'm asking is that the reference documentation for std::vectorstates that the push_back method comes in two overloads.

void push_back (const value_type& val);
void push_back (value_type&& val);

为什么是第一个版本(采用 const value_type&

Why is the first version (taking a const value_type&) needed?

推荐答案

相对于类模板的非模板方法,您必须要小心。您的成员插入本身不是模板。这是模板类的一种方法。

You have to be careful about function templates, versus non-template methods of class templates. Your member insert is not itself a template. It's a method of a template class.

Container<int> c;
c.insert(...);

我们很容易看到 T 是没有在第二行推论,因为它已经在第一行固定为 int ,因为 T 是模板参数类模板,而不是方法。

We can pretty easily see that T is not deduced on the second line, because it's already fixed to int on the first line, because T is a template parameter of the class, not the method.

类模板的非模板方法仅在实例化类后与常规方法有一种不同: t实例化,除非实际调用它们。这很有用,因为它允许模板类使用类型,而只有某些方法才有意义(STL容器中充满了这样的示例)。

Non-template methods of class templates, only differ from regular methods in one way, once the class has been instantiated: they aren't instantiated unless they are actually called. This is useful because it allows a template class to work with types, for which only some of the methods make sense (STL containers are full of examples like this).

最重要的是,在上面的示例中,由于 T 被固定为 int ,因此您的方法变为:

The bottom line is that in my example above, since T is fixed to int, your method becomes:

void insert(int&& obj) { storage[size++] = std::forward<int>(obj); }

这根本不是一个警告性引用,只是通过右值引用获取,即它仅绑定到右值。这就是为什么您通常会在 push_back 之类的程序中看到两个重载,一个重载左值,一个重载右值。

This is not a forwaring reference at all, but simply takes by rvalue reference, i.e. it only binds to rvalues. That is why you typically see two overloads for things like push_back, one for lvalues and one for rvalues.

这篇关于模板代码中的转发引用与const左值引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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