模板类中的设置程序

Setters in template classes

本文关键字:设置 程序      更新时间:2023-10-16

我正试图找到一种为模板类编写setter函数的好方法。对于非模板类,如果因为函数签名/实现取决于参数类型而显得微不足道。例如,如果参数类型为int,则下一个函数应该是最优的:

void MyClass::Set(int value)
{
    myValue = value;
}

如果参数类型是std::vector,那么下一个实现应该接近最优:

void MyClass::Set(std::vector<SomeType> value)
{
    std::swap(myValue, value);
}

由于将使用正确的构造函数(move或copy)来构造函数参数,并且不会发生不必要的复制,因此假设move构造函数的成本可以忽略不计。

正如您所看到的,当类型更改时,这两种实现都有缺点:如果第一个版本的类型更改为std::vector,则至少会制作一个不必要的副本,从而使实际成本增加2或3倍。如果在第二版本中将类型更改为int,则会进行2次不必要的复制,从而将实际成本增加3倍。

你能给我一个setter函数的好的通用实现吗(可能有重载)?对于用作参数的任何类型,它都应该是最优的/接近最优的

PS:我宁愿不使用std::enable_if来制作几个依赖于类型的setter,因为类会急剧增加。

您可以使用转发引用接受右值左值

template <typename T>
void Set(T && value) {
    myValue = std::forward<T>(value);
}

您应该有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);
}

如果调用方对参数使用{}初始化,或者调用方是函数的名称并且需要上下文,或者在其他一些情况下,前两种方法允许隐式转换很好地工作:它会工作。这通过"完美转发"解决了一些更大的烦恼。

第三种方法为operator=提供了前两种方法没有涵盖的重载处理。这是"完美转发"的一个例子,顾名思义,这是不完美的。

如果只使用第三个过载,最常见的问题是.Set( {construction args} )不起作用。

const&过载可能是多余的,但我不确定。

实例


// and sometimes:
template<class...Us>
void Set( Us&&...us ) {
  v = {std::forward<Us>(us)...};
}

第四个是模板集。在这种情况下(我们使用operator=),这实际上并不有用,但在某些情况下可能有用。例如,这种模板访问器有用,optional::value_or应该具有该重载。基本上,在您可以直接构造目标值的情况下,或者在其他参数可能导致您而不是构造值的情况中,这是有用的:如果您总是执行{std::forward<Us>(us)...},那么您还可以从外部使用.Set( {args...} ),它调用上面的第一个重载。