性能比较:of(std::string&) vs f(T&&)
Performance comparison: f(std::string&&) vs f(T&&)
我正在尝试了解使用 WidgetURef::setName
(URef
是通用参考,Scott Meyers 创造的术语)与 WidgedRRef::setName
(RRef
作为 R 值参考):
#include <string>
class WidgetURef {
public:
template<typename T>
void setName(T&& newName)
{
name = std::move(newName);
}
private:
std::string name;
};
class WidgetRRef {
public:
void setName(std::string&& newName)
{
name = std::move(newName);
}
private:
std::string name;
};
int main() {
WidgetURef w_uref;
w_uref.setName("Adela Novak");
WidgetRRef w_rref;
w_rref.setName("Adela Novak");
}
我很欣赏使用通用引用的人应该使用 std::forward
来代替,但这只是一个(不完美的)示例来突出有趣的一点。
问题
在此特定示例中,使用一种实现与另一种实现相比对性能有何影响?虽然 WidgetURef
需要类型推导,但它在其他方面与 WidgetRRef
相同,不是吗?至少在这个特定的场景中,在这两种情况下参数都是一个 r-value
引用,所以不会创建临时对象。这个推理正确吗?
上下文
该示例取自 Scott Meyers 的 "Effective Modern C++"(第 170 页)的第 25 项。根据这本书(假设我的理解是正确的!),采用通用引用 T&&
的版本不需要临时对象,而另一个采用 std::string&&
的版本需要。我真的不明白为什么。
In this particular example, what is the performance implications of using one implementation vs the other?
通用引用,如 Scott Meyers 所说,主要不是出于性能原因,而是笼统地说,以相同的方式处理 L- 和 Rvalue 引用以避免无数重载(并能够传播转发期间的所有类型信息)。
[...] so no temporaries are created. Is this reasoning correct?
右值引用不会阻止创建临时对象。右值引用是一种能够绑定到临时对象的引用(const 左值引用除外)!当然,在您的示例中,会有临时对象,但右值引用可以绑定到它。通用引用首先必须经历引用折叠,但最后,行为将与您的情况相同:
// explicitly created temporary
w_uref.setName(std::string("Adela Novak"));
// will create temporary of std::string --> uref collapses to rvalue ref
// so is effectively the same as
w_rref.setName("Adela Novak");
另一方面,通过使用右值引用,您可以隐式地强制临时,因为 std::string&&
无法绑定到该文字。
w_rref.setName("Adela Novak"); // need conversion
因此编译器将从字面量创建一个临时 std::string
右值引用然后可以绑定到。
I don't really see why.
在这种情况下,模板将解析为 const char(&)[12]
,因此与上述情况相比,不会创建 std::string
临时文件。因此,这样效率更高。
带有参数 "Adela Novak"
的 setName(T&& newName)
将 T 推断为 const char (&)[12]
,然后将其分配给 std::string
。
setName(std::string&& newName)
使用参数 "Adela Novak"
创建一个临时 std::string
对象,然后将其移动分配给 std::string
.
这里第一个效率更高,因为不涉及移动。
斯科特自己说 WidgetURef
"compiles, but is bad, bad, bad!"(逐字)。当您使用 std::move
而不是 std::forward
时,这两个 类 的行为不同: setName
因此可以修改其参数:
#include <string>
#include <iostream>
class WidgetURef {
public:
template<typename T>
void setName(T&& newName)
{
name = std::move(newName);
}
private:
std::string name;
};
int main() {
WidgetURef w_uref;
std::string name = "Hello";
w_uref.setName(name);
std::cout << "name=" << name << "\n";
}
可以轻松打印name=
,意思是name
的值被改变了。事实上,它至少在 ideone 上确实如此。
另一方面,WidgetRRef
要求传递的参数是一个右值引用,所以上面的例子如果没有明确的 setName(std::move(name))
.
如果您将 std::string
作为参数传递,WidgetURef
和 WidgetRRef
都不需要创建额外的副本。但是,如果您传递可以从 std::string
分配的内容(例如 const char*
),那么第一个示例将通过引用传递它并将其分配给字符串(除了从 C 复制数据外没有任何副本-style string into std::string
),第二个示例将首先创建一个临时 string
,然后将其作为右值引用传递给该方法。如果将 std::move(newName)
替换为正确的 std::forward<T>(newName)
.
,这些属性将保留
假设问题中所述的参数
template<typename T>
void setName(T&& newName)
{
name = std::forward<T>(newName);
}
将使用 const char *
参数
为数据成员 name
调用 std::string
赋值运算符
void setName(std::string&& newName)
{
name = std::move(newName);
}
调用 std::string
构造函数来创建一个临时的,Rvalue Ref 可以绑定到它。
使用std::string&&
参数
调用std::string
数据成员name
的移动赋值/构造函数
调用 std::string
析构函数来销毁我们从中移动数据的临时文件。
我正在尝试了解使用 WidgetURef::setName
(URef
是通用参考,Scott Meyers 创造的术语)与 WidgedRRef::setName
(RRef
作为 R 值参考):
#include <string>
class WidgetURef {
public:
template<typename T>
void setName(T&& newName)
{
name = std::move(newName);
}
private:
std::string name;
};
class WidgetRRef {
public:
void setName(std::string&& newName)
{
name = std::move(newName);
}
private:
std::string name;
};
int main() {
WidgetURef w_uref;
w_uref.setName("Adela Novak");
WidgetRRef w_rref;
w_rref.setName("Adela Novak");
}
我很欣赏使用通用引用的人应该使用 std::forward
来代替,但这只是一个(不完美的)示例来突出有趣的一点。
问题
在此特定示例中,使用一种实现与另一种实现相比对性能有何影响?虽然 WidgetURef
需要类型推导,但它在其他方面与 WidgetRRef
相同,不是吗?至少在这个特定的场景中,在这两种情况下参数都是一个 r-value
引用,所以不会创建临时对象。这个推理正确吗?
上下文
该示例取自 Scott Meyers 的 "Effective Modern C++"(第 170 页)的第 25 项。根据这本书(假设我的理解是正确的!),采用通用引用 T&&
的版本不需要临时对象,而另一个采用 std::string&&
的版本需要。我真的不明白为什么。
In this particular example, what is the performance implications of using one implementation vs the other?
通用引用,如 Scott Meyers 所说,主要不是出于性能原因,而是笼统地说,以相同的方式处理 L- 和 Rvalue 引用以避免无数重载(并能够传播转发期间的所有类型信息)。
[...] so no temporaries are created. Is this reasoning correct?
右值引用不会阻止创建临时对象。右值引用是一种能够绑定到临时对象的引用(const 左值引用除外)!当然,在您的示例中,会有临时对象,但右值引用可以绑定到它。通用引用首先必须经历引用折叠,但最后,行为将与您的情况相同:
// explicitly created temporary
w_uref.setName(std::string("Adela Novak"));
// will create temporary of std::string --> uref collapses to rvalue ref
// so is effectively the same as
w_rref.setName("Adela Novak");
另一方面,通过使用右值引用,您可以隐式地强制临时,因为 std::string&&
无法绑定到该文字。
w_rref.setName("Adela Novak"); // need conversion
因此编译器将从字面量创建一个临时 std::string
右值引用然后可以绑定到。
I don't really see why.
在这种情况下,模板将解析为 const char(&)[12]
,因此与上述情况相比,不会创建 std::string
临时文件。因此,这样效率更高。
"Adela Novak"
的 setName(T&& newName)
将 T 推断为 const char (&)[12]
,然后将其分配给 std::string
。
setName(std::string&& newName)
使用参数 "Adela Novak"
创建一个临时 std::string
对象,然后将其移动分配给 std::string
.
这里第一个效率更高,因为不涉及移动。
斯科特自己说 WidgetURef
"compiles, but is bad, bad, bad!"(逐字)。当您使用 std::move
而不是 std::forward
时,这两个 类 的行为不同: setName
因此可以修改其参数:
#include <string>
#include <iostream>
class WidgetURef {
public:
template<typename T>
void setName(T&& newName)
{
name = std::move(newName);
}
private:
std::string name;
};
int main() {
WidgetURef w_uref;
std::string name = "Hello";
w_uref.setName(name);
std::cout << "name=" << name << "\n";
}
可以轻松打印name=
,意思是name
的值被改变了。事实上,它至少在 ideone 上确实如此。
另一方面,WidgetRRef
要求传递的参数是一个右值引用,所以上面的例子如果没有明确的 setName(std::move(name))
.
如果您将 std::string
作为参数传递,WidgetURef
和 WidgetRRef
都不需要创建额外的副本。但是,如果您传递可以从 std::string
分配的内容(例如 const char*
),那么第一个示例将通过引用传递它并将其分配给字符串(除了从 C 复制数据外没有任何副本-style string into std::string
),第二个示例将首先创建一个临时 string
,然后将其作为右值引用传递给该方法。如果将 std::move(newName)
替换为正确的 std::forward<T>(newName)
.
假设问题中所述的参数
template<typename T>
void setName(T&& newName)
{
name = std::forward<T>(newName);
}
将使用 const char *
参数
name
调用 std::string
赋值运算符
void setName(std::string&& newName)
{
name = std::move(newName);
}
调用 std::string
构造函数来创建一个临时的,Rvalue Ref 可以绑定到它。
使用std::string&&
参数
std::string
数据成员name
的移动赋值/构造函数
调用 std::string
析构函数来销毁我们从中移动数据的临时文件。