我试图找到一种为模板类编写setter函数的好方法。对于非模板类,如果某种程度上是微不足道的,因为函数签名/实现依赖于参数类型。例如,如果参数类型为int
,则下一个函数应该是最佳的:
void MyClass::Set(int value)
{
myValue = value;
}
如果参数类型为std::vector
,则下一个实现应接近最佳:
void MyClass::Set(std::vector<SomeType> value)
{
std::swap(myValue, value);
}
作为正确的构造函数(移动或复制)将用于构造函数参数,并且不会发生不必要的复制,假设移动构造函数的成本可以忽略不计。
正如您所看到的,当更改类型时,两种实现都有缺点:如果第一个版本的类型更改为std::vector
,则至少会创建一个不必要的副本,将实际成本增加2倍或者3。
如果在第二个版本2中类型更改为int
,则会生成不必要的副本,从而将实际成本增加3倍。
你能否给我一个关于setter函数的一个很好的通用实现(可能有重载)?对于用作参数的任何类型,它应该是最佳的/接近最佳的。
PS:我宁愿不使用std::enable_if
制作几个类型相关的setter,因为类会急剧增加。
答案 0 :(得分:7)
您可以使用转发引用接受 rvalue 或左值,然后将其转发给相应的移动或复制赋值运算符:
template <typename T>
void Set(T && value) {
myValue = std::forward<T>(value);
}
答案 1 :(得分:0)
你应该有3次重载。
void Set( T&& t ) {
v = std::move(t);
}
void Set( T const& t ) {
v = t;
}
template<class U>
void Set( U&& u ) {
v = std::forward<U>(u);
}
如果调用者在参数上使用{}
初始化,或者如果调用者是函数的名称并且需要上下文或其他一些情况,则前两个允许隐式转换正常工作:将工作。这解决了一些更大的烦恼与#34;完美转发&#34;。
第三个为您提供了operator=
超载的任何内容,以处理前两个未涵盖的内容。这是&#34;完美转发&#34;的一个例子,顾名思义是不完美的。
如果您只使用第3次重载,则最常见的问题是.Set( {construction args} )
无法正常工作。
const&
重载可能是多余的,但我不确定。
// and sometimes:
template<class...Us>
void Set( Us&&...us ) {
v = {std::forward<Us>(us)...};
}
第四个是安置集。在这种情况下(我们使用operator=
),这实际上并没有用,但在某些情况下它可以。有关这样的emplace-accessor有用的示例,optional::value_or
应该具有该重载。基本上,您可以直接构建目标值的情况,或者其他参数可能导致您不构造值的情况,这很有用:如果您执行{std::forward<Us>(us)...}
总是也可能在外部使用.Set( {args...} )
,它调用上面的第一个重载。