STD :: MOVE或STD ::向前转向C 中的成员变量时
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
。
foo2
的构造函数总是代表适当的变量foo1<T>
的正确方法,因为需要转发到Foo1的构造函数?即使用std::forward
。
update
使用std::move
的foo1
失败:
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既是参考类型又是值类型。
这些示例均未使用通用引用(转发引用,如今被称为)。
转发引用仅在存在类型扣除的情况下形成,但是foo1
和foo2
的构造函数中的T&&
并未推导,因此它只是RVALUE参考。
由于两者都是rvalue参考,因此您应该在两者上使用std::move
。
如果要使用转发引用,则应使构造函数具有推论模板参数:
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
,因为客户端代码可以通过lvalue并使对象无效地无效:
std::vector<int> v {0,1,2};
foo1<std::vector<int>> foo = v;
std::cout << v[2]; //yay, undefined behaviour
一种更简单的方法是按值和无条件的std::move
进行存储:
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) }
{
}
};
对于完美的转发版本:
- 通过lvalue->一个副本
- 通过了rvalue->一个动作
按价值和移动版本进行通过:
- 通过了lvalue->一个副本,一个动作
- 通过rvalue->两个动作
考虑此代码需要的性能以及需要更改和维护的程度,并根据此选择选择一个选项。
这取决于您如何推断T
。例如:
template<class T>
foo1<T> make_foo1( T&& t ) {
return std::forward<T>(t);
}
在这种情况下,foo1<T>
中的T
是转发参考,您的代码不会编译。
std::vector<int> bob{1,2,3};
auto foo = make_foo1(bob);
上面的代码从bob
默默移动到构造函数中的std::vector<int>&
转移到foo1<std::vector<int>&>
。
使用foo2
进行同样的操作。您将获得foo2<std::vector<int>&>
,它将具有bob
的引用。
编写模板时,必须考虑将T
类型作为参考的含义。如果您的代码不支持它是参考,请考虑static_assert
或SFINAE阻止该案例。
template <typename T>
struct foo1 {
static_assert(!std::is_reference<T>{});
T t_;
foo1(T&& t) :
t_{ std::move(t) }
{
}
};
现在,此代码生成了一个合理的错误消息。
您可能会认为现有的错误消息还可以,但是只有可以,因为我们搬进了T
。
template <typename T>
struct foo1 {
static_assert(!std::is_reference<T>{});
foo1(T&& t)
{
auto internal_t = std::move(t);
}
};
在这里只有static_assert
确保我们的T&&
是实际的rvalue。
,但对于问题的理论列表就足够了。你有一个具体的。
最后,这可能是想要的:
template <class T> // typename is too many letters
struct foo1 {
static_assert(!std::is_reference<T>{});
T t_;
template<class U,
class dU=std::decay_t<U>, // or remove ref and cv
// SFINAE guard required for all reasonable 1-argument forwarding
// reference constructors:
std::enable_if_t<
!std::is_same<dU, foo1>{} && // does not apply to `foo1` itself
std::is_convertible<U, T> // fail early, instead of in body
,int> = 0
>
foo1(U&& u):
t_(std::forward<U>(u))
{}
// explicitly default special member functions:
foo1()=default;
foo1(foo1 const&)=default;
foo1(foo1 &&)=default;
foo1& operator=(foo1 const&)=default;
foo1& operator=(foo1 &&)=default;
};
或,在99/100情况下同样好的情况:
template <class T>
struct foo1 {
static_assert(!std::is_reference<T>{});
T t_;
foo1(T t) :
t_{ std::move(t) }
{}
// default special member functions, just because I never ever
// want to have to memorize the rules that makes them not exist
// or exist based on what other code I have written:
foo1()=default;
foo1(foo1 const&)=default;
foo1(foo1 &&)=default;
foo1& operator=(foo1 const&)=default;
foo1& operator=(foo1 &&)=default;
};
作为一般规则,这种更简单的技术导致1的移动量超过完美的转发技术,以换取大量的代码和复杂性。它允许{}
T t
参数的初始化给您的构造函数,这很好。
- std::ofstream 作为类成员删除复制构造函数?
- 使用 std::index_sequence 初始化具有固定大小数组成员的 POD 结构容器
- std::to_string - 'to_string' 不是 'std' 的成员 - Visual Studio Code 1.42.0
- 使用 std::async 时死锁,将来作为成员
- 可变参数模板参数扩展 类型为 std::function 的类成员
- FreeFileSync C++错误:'byte'不是 'std' 的成员
- 为什么 std::sort 找不到合适的(静态成员)函数重载?
- 对带有唯一指针的 std::thread 使用类成员函数时出现编译错误
- 在模板化成员函数的返回类型中使用 std::enable_if 时的编译器差异
- 有没有办法避免为 std::variant 类成员中的所有类型编写构造函数?
- 是否允许类类型的 std::function 成员变量(不完整类型)?
- 访问 std:vector 的类成员 std:vector 在一个类中与另一个 std:vector
- std::异步与非静态成员函数
- 如何在 c++11 中静态断言 std::array 类成员进行排序?
- 将初始化器列表/聚合初始化转发到 std::array 成员
- C++ IDE 不会推断/自动完成对模板类中的 std::array 下标表达式的成员访问
- 在哪里可以找到 std::bitset 的数据成员?
- 没有'str'成员在 GCC 和 Clang 'std::basic_ostream<char>',但 MSVC 没有问题
- unique_ptr问题:不是'std'成员
- std::成员函数的异步调用