我的通用模板应该使用 T 还是 T&&?

Should my generic template use T or T&&?

本文关键字:还是 我的      更新时间:2023-10-16

我正在处理一个具有许多重载的给定函数的文件,如下所示:

inline X f(ScriptWrappable* impl, Y y, Z z) { ... }
inline X f(Node* impl, Y y, Z z) { ... }
inline X f(RawPtr<T> impl, Y y, Z z) { ... }
inline X f(const RefPtr<T>& impl, Y y, Z z) { ... }
inline X f(ScriptWrappable* impl, Y y, Z z) { ... }
inline X f(const String& impl, Y y, Z z) { ... }
inline X f(int64_t impl, Y y, Z z) { ... }
template<typename T, size_t capacity> inline X f(const Vector<T, capacity>& impl, Y y, Z z) { ... }

我正在尝试添加一个只需要一个参数的新重载,如下所示:

template<typename T> inline X f(T impl, W w) {
  return f(impl, w->getY(), w->getZ());
}

我正在使用模板,以便上述所有变体都自动与我的新双参数版本一起使用。

然而,在代码审查期间,有人问我,"T&&避免复制更好吗?也就是说,我应该改为做

template<typename T> inline X f(T&& impl, W w) {
  return f(impl, w->getY(), w->getZ());
}

我真的不知道这个问题的答案。我以为我理解了通用参考,但我不熟悉它们什么时候是一个好主意。如果我选择其中一个而不是另一个,会有什么后果?

如果我不得不猜测,我会说,由于T可以代表的原始类型都很容易复制(原语,引用或指针),T&&&不会带来太多好处。但我仍然很好奇,例如,是否可以将任何类型传递给 T&& 版本,而不能传递给 T 版本,反之亦然。

你应该像这样编写你的函数:

template<typename T> inline X f(T&& impl, W w) {
  return f(std::forward<T>(impl), w->getY(), w->getZ());
}

这称为完美转发。impl的类型将与直接呼叫f完全相同。它不一定是 r 值引用。

答案有点复杂,因为它需要对编译器关于模板的规则有所了解。

在以下声明中:

template<class T> void func(T&& t);

T 在推导的上下文中计算,因此编译器将被视为 r 值引用或 l 值引用,以适当者为准。再加上使用std::forward(注意:在这种情况下不是std::move),这导致了完美的转发并且是最佳的效率。

但是,这并没有在推导的上下文中进行评估:

template<class T>
struct X {
  void foo(T&& t);
};

在这种情况下,foo 实际上需要一个 r 值引用。

这也不是推导的上下文:

template<class T>
void foo(std::vector<T>&& v);

在这两种情况下,应使用 std::move

相关文章: