C++ 在C++;
我想写一个模板函数,通过移动或复制来接收参数。 我使用的最有效的方法是:C++ 在C++;,c++,performance,c++11,c++17,C++,Performance,C++11,C++17,我想写一个模板函数,通过移动或复制来接收参数。 我使用的最有效的方法是: void setA(A a) { m_a = std::move(a); } 在这里,当我们使用的是 A a; setA(a); // <<---- one copy ctor & one move ctor setA(std::move(a)); // <<---- two move ctors 会省很多钱 A a; setA(a);
void setA(A a)
{
m_a = std::move(a);
}
在这里,当我们使用的是
A a;
setA(a); // <<---- one copy ctor & one move ctor
setA(std::move(a)); // <<---- two move ctors
会省很多钱
A a;
setA(a); // <<---- one copy ctor
setA(std::move(a)); // <<---- one move ctor
有人有什么想法吗?
谢谢 最好是在构造器中就地构造
a
。关于二传手,没有一个是最好的。在大多数情况下,按价值衡量和搬家似乎效果不错,但有时效率较低。如您所示,重载效率最高,但会导致大量代码重复。模板可以在通用引用的帮助下避免代码重复,但随后必须进行自己的类型检查,这会变得很复杂。除非您已经用分析器检测到这是一个瓶颈,否则我建议您坚持按值取然后移动,因为这是最简单的,会导致最小的代码重复,并提供良好的异常安全性。两个setter版本setA(a&&a)
和setA(const a&a)
可以使用(又称完美转发):
除了更传统的“推送”/“添加”式调用外,标准库还经常提供“安放”式调用。例如,获取构造元素所需的参数,并在向量内部构造一个,而无需复制或移动任何内容。简短回答:
这是冗长和速度之间的折衷。速度不是一切
以这种方式定义它,有两个函数…将节省很多
A a;
setA(a); // <<---- one copy ctor
setA(std::move(a)); // <<---- one move ctor
它将保存一个移动分配,这通常不是很多
除非您需要这段特定的代码尽可能快(例如,您正在编写一个自定义容器),否则我更喜欢按值传递,因为它不太冗长
其他可能的办法包括:
- 使用转发引用,正如其他答案中所建议的那样。它将为您提供与一对重载(
+const T&
)相同的复制/移动量,但它使传递多个参数变得更容易,因为您只需编写一个函数,而不必编写其中的2N个T&&
- 使setter的行为类似于。这不会给您带来任何性能好处(因为您正在为现有对象赋值,而不是创建一个新对象),因此没有多大意义
- 经过大量研究,我找到了答案
我创建了一个高效的包装器类,它允许您保存这两个选项,并允许您在内部函数中决定是否要复制
#pragma pack(push, 1)
template<class T>
class CopyOrMove{
public:
CopyOrMove(T&&t):m_move(&t),m_isMove(true){}
CopyOrMove(const T&t):m_reference(&t),m_isMove(false){}
bool hasInstance()const{ return m_isMove; }
const T& getConstReference() const {
return *m_reference;
}
T extract() && {
if (hasInstance())
return std::move(*m_move);
else
return *m_reference;
}
void fastExtract(T* out) && {
if (hasInstance())
*out = std::move(*m_move);
else
*out = *m_reference;
}
private:
union
{
T* m_move;
const T* m_reference;
};
bool m_isMove;
};
#pragma pack(pop)
#pragma包(推送,1)
模板
类CopyOrMove{
公众:
CopyOrMove(T&&T):m_move(&T),m_isMove(true){}
CopyOrMove(const T&T):m_reference(&T),m_isMove(false){}
bool hasInstance()常量{return m_isMove;}
常量T&getConstReference()常量{
返回*m_参考;
}
T extract()&&{
if(hasInstance())
返回标准::移动(*m_移动);
其他的
返回*m_参考;
}
无效快速提取(T*out)和{
if(hasInstance())
*out=std::move(*m_move);
其他的
*out=*m_参考;
}
私人:
联盟
{
T*m_移动;
常数T*m_参考;
};
布尔·穆伊斯莫夫;
};
#布拉格语包(流行语)
现在您可以拥有以下功能:
void setAAndBAndCAndDAndEAndF(CopyOrMove<A> a, CopyOrMove<B> b, CopyOrMove<C> c, CopyOrMove<D> d, CopyOrMove<E> e, CopyOrMove<F> f)
void seta和bandcandeandf(copyrmove a、copyrmove b、copyrmove c、copyrmove d、copyrmove e、copyrmove f)
零代码重复!并且没有多余的复制或移动!考虑一下
void setA(A){std::swap(m_A,A);}
。最有效的方法是在构造函数中使用完美的转发。@WhozCraig:为什么?用m_A
的先前内容填充A
而不是将其保留为“空”有什么好处,当函数退出时它将被销毁?如果您有一个简单的setter函数,可以将其设置为任何值,那么您可以使用公共数据成员。然后您可以只编写obj.a=std::move(a);
或obj.a=a;
只需一次复制或移动。此外,如果您使setter内联或进行了整个程序优化,并且移动构造函数没有副作用(不应该有副作用),则第二次移动可以优化。“最有效”就是没有一个一开始就需要10个参数的函数。除了参数的副本,在其他地方也会有伤害。只是说。。。
struct A {
A(int x) : m_x(x) {}
int m_x;
};
struct B {
template<typename T>
B(T&& a) : m_a(std::forward<T>(a)) {}
A m_a;
};
int main() {
B b{ 1 }; // zero copies/moves
}
#pragma pack(push, 1)
template<class T>
class CopyOrMove{
public:
CopyOrMove(T&&t):m_move(&t),m_isMove(true){}
CopyOrMove(const T&t):m_reference(&t),m_isMove(false){}
bool hasInstance()const{ return m_isMove; }
const T& getConstReference() const {
return *m_reference;
}
T extract() && {
if (hasInstance())
return std::move(*m_move);
else
return *m_reference;
}
void fastExtract(T* out) && {
if (hasInstance())
*out = std::move(*m_move);
else
*out = *m_reference;
}
private:
union
{
T* m_move;
const T* m_reference;
};
bool m_isMove;
};
#pragma pack(pop)
void setAAndBAndCAndDAndEAndF(CopyOrMove<A> a, CopyOrMove<B> b, CopyOrMove<C> c, CopyOrMove<D> d, CopyOrMove<E> e, CopyOrMove<F> f)