是否前进或移动;如何确定在 class 的用法中哪个是首选?
To forward or move or not; how to determine which is preferred within the context of a class's usage?
class Test {
public:
typedef std::set<std::pair<double, double>> DataSet;
explicit Test(const DataSet&& d) {
for (auto &itr : d) {
std::cout << "value1 = " << itr.first << '\n';
std::cout << "value2 = " << itr.second << '\n';
}
}
};
int main() {
//using namespace util;
try {
// Forwarding
Test obj( std::forward<Test::DataSet>(
{
{ 10.0, 20.0 },
{ 30.0, 40.0 },
{ 50.0, 60.0 }
}
));
std::cout << '\n';
// Move
Test obj2(std::move(Test::DataSet(
{
{ 10.0, 20.0 },
{ 30.0, 40.0 },
{ 50.0, 60.0 }
}
)
));
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
都没有。
Test::DataSet(/*...*/)
已经是可以绑定到右值引用的右值。 std::move
所做的唯一一件事就是将左值转换为 xvalue(它是右值),以便它可以绑定到右值引用,而左值不能。所以 std::move
是多余的。
std::forward
只在其模板参数上做同样的事情。所以它也是多余的(充其量)。
const DataSet&&
作为参数没有意义。您想要仅绑定到右值的 DataSet&&
或绑定到右值和左值的 const DataSet&
。
如果您想利用传递的引用可能是右值这一事实(即从其成员中移出),那么您需要对函数进行两次重载。一种采用 DataSet&&
,使用右值参数调用,另一种采用 const DataSet&
,使用左值参数调用。
在您问题中 Test
函数的特定情况下,我看不出从成员中移动或对 DataSet
状态进行任何其他修改会有什么好处,所以我会简单地使用 const DataSet&
作为参数,而不会为 std::move
ing 或 std::forward
ing 专门进入该函数而烦恼。
[根据评论编辑的代码示例]
ISO 核心指南中 advice 的有用信息。这表明您应该更喜欢 (const T&),但如果您真的需要速度,请专门添加一个 (T&&)。这样你就可以将小集合作为(L-Value)
const auto data = Test::DataSet{
{ 10.0, 20.0 },
{ 30.0, 40.0 },
{ 50.0, 60.0 }};
Test obj( data );
或(R 值)
Test obj( Test::DataSet{
{ 10.0, 20.0 },
{ 30.0, 40.0 },
{ 50.0, 60.0 },
});
添加额外的移动使第二种调用更有效,但建议仅在需要时才使用这种类型的优化。您应该避免一个构造函数与另一个构造函数具有不同的语义,避免这种情况的最简单方法是只有一个构造函数。
就个人而言,即使经过 20 多年的 C++ 编程,我发现 link 中的 table 非常有用,我将其打印出来并贴在我的显示器上。
class Test {
public:
typedef std::set<std::pair<double, double>> DataSet;
explicit Test(const DataSet&& d) {
for (auto &itr : d) {
std::cout << "value1 = " << itr.first << '\n';
std::cout << "value2 = " << itr.second << '\n';
}
}
};
int main() {
//using namespace util;
try {
// Forwarding
Test obj( std::forward<Test::DataSet>(
{
{ 10.0, 20.0 },
{ 30.0, 40.0 },
{ 50.0, 60.0 }
}
));
std::cout << '\n';
// Move
Test obj2(std::move(Test::DataSet(
{
{ 10.0, 20.0 },
{ 30.0, 40.0 },
{ 50.0, 60.0 }
}
)
));
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
都没有。
Test::DataSet(/*...*/)
已经是可以绑定到右值引用的右值。 std::move
所做的唯一一件事就是将左值转换为 xvalue(它是右值),以便它可以绑定到右值引用,而左值不能。所以 std::move
是多余的。
std::forward
只在其模板参数上做同样的事情。所以它也是多余的(充其量)。
const DataSet&&
作为参数没有意义。您想要仅绑定到右值的 DataSet&&
或绑定到右值和左值的 const DataSet&
。
如果您想利用传递的引用可能是右值这一事实(即从其成员中移出),那么您需要对函数进行两次重载。一种采用 DataSet&&
,使用右值参数调用,另一种采用 const DataSet&
,使用左值参数调用。
在您问题中 Test
函数的特定情况下,我看不出从成员中移动或对 DataSet
状态进行任何其他修改会有什么好处,所以我会简单地使用 const DataSet&
作为参数,而不会为 std::move
ing 或 std::forward
ing 专门进入该函数而烦恼。
[根据评论编辑的代码示例]
ISO 核心指南中 advice 的有用信息。这表明您应该更喜欢 (const T&),但如果您真的需要速度,请专门添加一个 (T&&)。这样你就可以将小集合作为(L-Value)
const auto data = Test::DataSet{
{ 10.0, 20.0 },
{ 30.0, 40.0 },
{ 50.0, 60.0 }};
Test obj( data );
或(R 值)
Test obj( Test::DataSet{
{ 10.0, 20.0 },
{ 30.0, 40.0 },
{ 50.0, 60.0 },
});
添加额外的移动使第二种调用更有效,但建议仅在需要时才使用这种类型的优化。您应该避免一个构造函数与另一个构造函数具有不同的语义,避免这种情况的最简单方法是只有一个构造函数。
就个人而言,即使经过 20 多年的 C++ 编程,我发现 link 中的 table 非常有用,我将其打印出来并贴在我的显示器上。