将正在移动的对象的成员作为参数传递是否安全
Is it safe to pass as argument a member of an object which is moving
#include <iostream>
#include <string>
#include <map>
struct A {
int n { 42 };
std::string s { "ciao" };
};
int main() {
A a;
std::map<std::string, A> m;
std::cout << "a.s: " << a.s << std::endl; // print: "a.s: ciao"
m.emplace(a.s, std::move(a)); // a.s is a member of a, moved in the same line
std::cout << "in map: " << m.count("ciao") << std::endl; // print: "in map: 1"
std::cout << "a.s: " << a.s << std::endl; // print: "a.s: " (as expected, it has been moved)
}
将"移动"对象的成员作为参数传递是否安全?在这种情况下,emplace似乎有效:地图具有预期的键。
有趣。我认为这是安全的,原因错综复杂。(为了记录,我也认为这是非常糟糕的风格 - 显式副本在这里不花钱,因为它将被移动到地图中。
首先,实际的函数调用不是问题。 std::move
仅将a
强制转换为右值引用,右值引用只是引用;a 不会立即移动。 emplace_back
将其参数转发给std::pair<std::string, A>
的构造函数,这就是事情变得有趣的地方。
那么,使用std::pair
的哪个构造函数呢?它有相当多的,但有两个是相关的:
pair(const T1& x, const T2& y);
template<class U, class V> pair(U&& x, U&&y);
(参见标准中的 20.3.2(,其中 T1
和 T2
是 std::pair
的模板参数。根据 13.3,我们最终以 U == const T1&
和 V == T2
进入后者,这很直观(否则实际上不可能进入std::pair
(。这给我们留下了一个形式的构造函数
pair(const T1& x, T2 &&y) : first(std::forward(x)), second(std::forward(y)) { }
根据 20.3.2 (6-8(。
那么,这安全吗?有用的是,std::pair
详细定义,包括内存布局。它特别指出,
T1 first;
T2 second;
按此顺序出现,因此first
将在second
之前初始化。这意味着在您的特定情况下,字符串将在移走之前被复制,并且您是安全的。
但是,如果您以相反的方式执行此操作:
m.emplace(std::move(A.s), A); // huh?
。然后你会得到有趣的效果。
调用 m.emplace(a.s, std::move(a));
时发生的情况是,您将 l 值引用传递给 a.s
,将 r 值引用传递给 emplace 函数a
。这是否安全取决于该函数的实现。如果它首先从第二个参数中剔除胆量,然后尝试使用第一个参数,您可能会遇到问题。如果它首先使用第一个参数,然后从第二个参数中剔除胆量,没问题。由于这是一个标准的库函数,你不能依赖实现,所以我们不能断定代码是安全的。
为了安全起见,您可以确保在将字符串的内容移出a
之前创建字符串的副本。
m.emplace(std::string(a.s), std::move(a));
现在,您将 r 值引用传递给 a.s
的临时副本,并将 r 值引用传递给 emplace 函数a
。因此,在这种情况下它是安全的,因为std::map::emplace
采用一组通用引用。如果你对一个函数做同样的事情,它按值接受第二个参数,这是不安全的,因为移动构造函数可以在复制a.s
之前被调用(因为函数参数的计算顺序未指定(。知道这一点,这段代码可能太聪明了,无法维护。
因此,最清晰的代码是这样的:
auto key = a.s;
m.emplace(std::move(key), std::move(a));
- 是否有C++编译器选项允许激进地删除所有函数调用,并将参数传递给具有空体的函数
- 是否可以在不填充自己的参数的情况下将模板函数作为参数传递?
- 是否可以在命令行中将输入参数传递给可执行文件
- 在C++17中,lambda是否可以作为模板参数传递
- 使用 std::move 将参数传递给函数,如果该参数声明为按值传递或使用移动操作数 &&,是否有区别?
- 变量模板是否可以作为模板模板参数传递
- 是否可以将结构的成员变量作为参数传递
- 是否可以交换在 c++ 中作为函数 func(say) 中的参数传递的 const 向量的两个元素,例如:vector<int>func (const vector<int>&
- 是否可以在不使用数组的情况下将不同数量的相同类型的参数传递给函数?
- 是否可以创建一个指向对象的智能指针并将此智能指针作为参数传递给构造函数?
- 是否可以将变量/函数返回作为模板参数传递
- 继承是否是将一组模型参数传递给不同类的可行解决方案
- 是否可以使用将不同参数传递给构造函数的类的不同实例初始化向量
- 是否可以将char **作为参数传递
- 是否可以将 std::d eque 的成员函数作为参数传递?
- 是否可以将可变长度数组作为参数传递C++?
- 将模板原型作为模板参数传递 - 是否可能
- 将正在移动的对象的成员作为参数传递是否安全
- 将指向函数的指针数组作为"int"参数传递是否可以将地址转换为整数?
- 输入参数传递:是否存在有效的按值传递的大小阈值