在C ++中将通用构造函数分配给成员变量时,std :: move或std :: forward [英] std::move or std::forward when assigning universal constructor to member variable in C++
问题描述
请考虑以下类foo1
和foo2
template <typename T>
struct foo1
{
T t_;
foo1(T&& t) :
t_{ std::move(t) }
{
}
};
template <typename T>
struct foo2
{
foo1<T> t_;
foo2(T&& t) :
t_{ std::forward<T>(t) }
{
}
};
是否总是总是foo1
的构造函数表示初始化成员变量T
的正确方法?即使用std::move
.
Is it always the case that the constructor of foo1
represents the correct way to initialise the member variable T
? i.e. by using std::move
.
由于需要转发到foo1的构造函数,因此foo2
的构造函数是否总是代表初始化成员变量foo1<T>
的正确方法?即使用std::forward
.
Is it always the case that the constructor of foo2
represents the correct way to initialise the member variable foo1<T>
due to needing to forward to foo1's constructor? i.e. by using std::forward
.
更新
对于使用std::move
的foo1
,以下示例失败:
The following example fails for foo1
using std::move
:
template <typename T>
foo1<T> make_foo1(T&& t)
{
return{ std::forward<T>(t) };
}
struct bah {};
int main()
{
bah b;
make_foo1(b); // compiler error as std::move cannot be used on reference
return EXIT_SUCCESS;
}
这是一个问题,因为我希望T既是引用类型又是值类型.
Which is a problem as I want T to be both a reference type and a value type.
推荐答案
这些示例都没有使用通用引用(转发引用,现在称为它们).
Neither of these examples use universal references (forwarding references, as they are now called).
仅在存在类型推导的情况下形成转发引用,但在foo1
和foo2
的构造函数中未推导T&&
,因此它只是一个右值引用.
Forwarding references are only formed in the presence of type deduction, but T&&
in the constructors for foo1
and foo2
is not deduced, so it's just an rvalue reference.
由于两者都是右值引用,因此您都应在两者上均使用std::move
.
Since both are rvalue references, you should use std::move
on both.
如果要使用转发引用,则应使构造函数具有推导的模板参数:
If you want to use forwarding references, you should make the constructors have a deduced template argument:
template <typename T>
struct foo1
{
T t_;
template <typename U>
foo1(U&& u) :
t_{ std::forward<U>(u) }
{
}
};
template <typename T>
struct foo2
{
foo1<T> t_;
template <typename U>
foo2(U&& u) :
t_{ std::forward<U>(u) }
{
}
};
在这种情况下,您不应在foo1
中使用std::move
,因为客户端代码可能会传递一个左值,并使该对象无声地失效:
You should not use std::move
in foo1
in this case, as client code could pass an lvalue and have the object invalidated silently:
std::vector<int> v {0,1,2};
foo1<std::vector<int>> foo = v;
std::cout << v[2]; //yay, undefined behaviour
一种更简单的方法是按值无条件地将std::move
放入存储中:
A simpler approach would be to take by value and unconditionally std::move
into the storage:
template <typename T>
struct foo1
{
T t_;
foo1(T t) :
t_{ std::move(t) }
{
}
};
template <typename T>
struct foo2
{
foo1<T> t_;
foo2(T t) :
t_{ std::move(t) }
{
}
};
对于完美的转发版本:
- 传递的左值->一本
- 传递的右值->一招
对于按值传递和移动版本:
For the pass by value and move version:
- 传递的左值->一份,一招
- 传递的右值->两步走
请考虑该代码的性能要求以及需要更改和维护的代码量,然后基于此选择一个选项.
Consider how performant this code needs to be and how much it'll need to be changed and maintained, and choose an option based on that.
这篇关于在C ++中将通用构造函数分配给成员变量时,std :: move或std :: forward的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!