通用参考c++11代码重复
universal reference c++11 code duplication
在我的项目中我有这样的功能:
bool VectorList::put(const Pair &p);
这会通过复制 Pair
将 Pair
添加到 VectorList
。
我可以这样使用它:
Pair p { "key", "value" };
VectorList v;
v.put(p);
// or
v.put(Pair{ "anotherkey", "anothervalue" });
但是在第二种情况下创建了一个不必要的对象,所以我想做
bool VectorList::put(Pair &&p);
我检查了这是如何在 vector (gcc, llvm) 中完成的,两种方法中的代码 100% 相同,除了 equal / std::move() 行。
有什么方法可以避免代码重复吗?
put()
看起来类似于:
struct Node{
Pair pair;
AdditionalThings at;
};
bool VectorList::put(const Pair &p){
if (not_good_for_insert(p))
return false;
// ...
Node node = create_node();
node.pair = p;
// ...
return true;
}
是,使用 perfect forwarding:
template <typename P>
bool VectorList::put (P &&p) {
//can't forward p here as it could move p and we need it later
if (not_good_for_insert(p))
return false;
// ...
Node node = create_node();
node.pair = std::forward<P>(p);
// ...
return true;
}
另一种可能性是像 那样按值传递。完美转发版本的优点是,如果您传入兼容的参数,它不需要中间转换,并且如果移动成本高,则性能更好。缺点是转发引用函数非常贪婪,所以其他重载可能不会按照您的意愿行事。
请注意,Pair &&p
不是通用引用,它只是一个右值引用。通用(或转发)引用需要推导上下文中的右值,例如模板参数。
理想的解决方案是按照 TartanLlama 的建议接受通用引用。
如果您负担得起 header 文件中的函数定义,那么理想的解决方案是有效的。如果您的函数定义不能在 header 中公开(例如,您使用 Pimpl 惯用语或 interface-based 设计,或者函数驻留在共享库中),第二个最佳选择是按值接受。这样调用者可以选择如何构造参数(复制、移动、统一初始化)。不过,被叫方将不得不付出一步的代价。例如。 bool VectorList::put(Pair p);
:
VectorList v;
Pair p { "key", "value" };
v.put(p);
v.put(std::move(p));
v.put(Pair{ "anotherkey", "anothervalue" });
v.put({ "anotherkey", "anothervalue" });
在实现中,您从参数中移出:
bool VectorList::put(Pair p) { container_.push_back(std::move(p)); }
另一个评论是您可能喜欢坚持使用标准的 C++ 名称来进行容器操作,例如 push_back/push_front
,以便清楚它的作用。 put
晦涩难懂,需要代码的读者查看源代码或文档以了解发生了什么。
在 TartanLlama 的帮助下,我编写了以下测试代码:
#include <utility>
#include <iostream>
#include <string>
class MyClass{
public:
MyClass(int s2) : s(s2){
std::cout << "c-tor " << s << std::endl;
}
MyClass(MyClass &&other) : s(other.s){
other.s = -1;
std::cout << "move c-tor " << s << std::endl;
}
MyClass(const MyClass &other) : s(other.s){
std::cout << "copy c-tor " << s << std::endl;
}
~MyClass(){
std::cout << "d-tor " << s << std::endl;
}
public:
int s;
};
// ==============================
template <typename T>
MyClass process(T &&p){
MyClass out = std::forward<T>(p);
return out;
}
// ==============================
void t1(){
MyClass out = process( 100 );
}
void t2(){
MyClass out = process( MyClass(100) );
}
void t3(){
MyClass in = 100;
MyClass out = process(std::move(in));
std::cout << in.s << std::endl;
std::cout << out.s << std::endl;
}
void t4(){
MyClass in = 100;
MyClass out = process(in);
std::cout << in.s << std::endl;
std::cout << out.s << std::endl;
}
int main(int argc, char** argv){
std::cout << "testing fast c-tor" << std::endl; t1(); std::cout << std::endl;
std::cout << "testing c-tor" << std::endl; t2(); std::cout << std::endl;
std::cout << "testing move object" << std::endl; t3(); std::cout << std::endl;
std::cout << "testing normal object" << std::endl; t4(); std::cout << std::endl;
}
gcc 的输出如下:
testing fast c-tor
c-tor 100
d-tor 100
testing c-tor
c-tor 100
move c-tor 100
d-tor -1
d-tor 100
testing move object
c-tor 100
move c-tor 100
-1
100
d-tor 100
d-tor -1
testing normal object
c-tor 100
copy c-tor 100
100
100
d-tor 100
d-tor 100
在我的项目中我有这样的功能:
bool VectorList::put(const Pair &p);
这会通过复制 Pair
将 Pair
添加到 VectorList
。
我可以这样使用它:
Pair p { "key", "value" };
VectorList v;
v.put(p);
// or
v.put(Pair{ "anotherkey", "anothervalue" });
但是在第二种情况下创建了一个不必要的对象,所以我想做
bool VectorList::put(Pair &&p);
我检查了这是如何在 vector (gcc, llvm) 中完成的,两种方法中的代码 100% 相同,除了 equal / std::move() 行。
有什么方法可以避免代码重复吗?
put()
看起来类似于:
struct Node{
Pair pair;
AdditionalThings at;
};
bool VectorList::put(const Pair &p){
if (not_good_for_insert(p))
return false;
// ...
Node node = create_node();
node.pair = p;
// ...
return true;
}
是,使用 perfect forwarding:
template <typename P>
bool VectorList::put (P &&p) {
//can't forward p here as it could move p and we need it later
if (not_good_for_insert(p))
return false;
// ...
Node node = create_node();
node.pair = std::forward<P>(p);
// ...
return true;
}
另一种可能性是像
请注意,Pair &&p
不是通用引用,它只是一个右值引用。通用(或转发)引用需要推导上下文中的右值,例如模板参数。
理想的解决方案是按照 TartanLlama 的建议接受通用引用。
如果您负担得起 header 文件中的函数定义,那么理想的解决方案是有效的。如果您的函数定义不能在 header 中公开(例如,您使用 Pimpl 惯用语或 interface-based 设计,或者函数驻留在共享库中),第二个最佳选择是按值接受。这样调用者可以选择如何构造参数(复制、移动、统一初始化)。不过,被叫方将不得不付出一步的代价。例如。 bool VectorList::put(Pair p);
:
VectorList v;
Pair p { "key", "value" };
v.put(p);
v.put(std::move(p));
v.put(Pair{ "anotherkey", "anothervalue" });
v.put({ "anotherkey", "anothervalue" });
在实现中,您从参数中移出:
bool VectorList::put(Pair p) { container_.push_back(std::move(p)); }
另一个评论是您可能喜欢坚持使用标准的 C++ 名称来进行容器操作,例如 push_back/push_front
,以便清楚它的作用。 put
晦涩难懂,需要代码的读者查看源代码或文档以了解发生了什么。
在 TartanLlama 的帮助下,我编写了以下测试代码:
#include <utility>
#include <iostream>
#include <string>
class MyClass{
public:
MyClass(int s2) : s(s2){
std::cout << "c-tor " << s << std::endl;
}
MyClass(MyClass &&other) : s(other.s){
other.s = -1;
std::cout << "move c-tor " << s << std::endl;
}
MyClass(const MyClass &other) : s(other.s){
std::cout << "copy c-tor " << s << std::endl;
}
~MyClass(){
std::cout << "d-tor " << s << std::endl;
}
public:
int s;
};
// ==============================
template <typename T>
MyClass process(T &&p){
MyClass out = std::forward<T>(p);
return out;
}
// ==============================
void t1(){
MyClass out = process( 100 );
}
void t2(){
MyClass out = process( MyClass(100) );
}
void t3(){
MyClass in = 100;
MyClass out = process(std::move(in));
std::cout << in.s << std::endl;
std::cout << out.s << std::endl;
}
void t4(){
MyClass in = 100;
MyClass out = process(in);
std::cout << in.s << std::endl;
std::cout << out.s << std::endl;
}
int main(int argc, char** argv){
std::cout << "testing fast c-tor" << std::endl; t1(); std::cout << std::endl;
std::cout << "testing c-tor" << std::endl; t2(); std::cout << std::endl;
std::cout << "testing move object" << std::endl; t3(); std::cout << std::endl;
std::cout << "testing normal object" << std::endl; t4(); std::cout << std::endl;
}
gcc 的输出如下:
testing fast c-tor
c-tor 100
d-tor 100
testing c-tor
c-tor 100
move c-tor 100
d-tor -1
d-tor 100
testing move object
c-tor 100
move c-tor 100
-1
100
d-tor 100
d-tor -1
testing normal object
c-tor 100
copy c-tor 100
100
100
d-tor 100
d-tor 100