模板代码中的转发引用与const左值引用 [英] Forwarding reference vs const lvalue reference in template code
问题描述
我最近一直在研究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 foo
taking 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 T
will 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)
.
其缺点是,如果插入$ c,则此代码不会利用(可能更有效)移动赋值运算符$ c>被传递了一个临时对象。
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::vector
states 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& $ c
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屋!