按值传递还是通用引用

11
我是一名有用的助手,可以为您翻译文本。

我希望开发一个带有类型抹除的小型多态类,想知道哪个版本的模板化构造函数更好,应该使用哪个。

我们可以通过值传递:

class A
{
    ...
    template< typename T >
    A( T t ) { /* create the underlying model via std::move */ }
    ...
};

或者我们可以使用一个通用引用:
class A
{
    ...
    template< typename T >
    A( T &&t ) { /* create the underlying model via std::forward */ }
    ...
};

如果T不是类本身且该类未被复制,则必须启用通用引用。 有什么想法吗? 对我来说,两个版本看起来都一样。

可能是重复的问题:如何正确传递参数? 或者 https://dev59.com/f2Yq5IYBdhLWcg3wzDrk - stijn
2
不,第一个版本中你可以移动t。 - headmyshoulder
2
@jogojapan 请阅读我发布的链接:如果您打算制作副本,使用按值传递实际上是有意义的。 - stijn
@stijn 不确定你具体指的是什么。但请注意,我在谈论的是函数局部副本与数据成员中的副本之间的区别。仅仅为了将其复制到数据成员中而创建一个函数局部副本并不合理。至少乍一看是这样。 - jogojapan
@jogojapan 我改口了,我没有意识到你(也许还有OP?)在谈论复制函数本地副本的问题 - 这确实没有太多意义。 - stijn
显示剩余2条评论
1个回答

9
这两种方式并不等价,有时候需要优先选择其中一种。Nicolai Josuttis的精彩演讲专门探讨了这个话题,建议至少观看一次。
个人建议,除非遇到特殊情况,例如转换开销很大且想尽可能避免使用临时变量,否则建议仅通过值传递并使用std::move移动参数。 T&&更高效的情况:
struct foo {
    std::string bar;

    template <typename T>
    foo(T&& t)
        :bar(std::forward<T>(t)){}
};

对比:

struct foo {
    std::string bar;

    foo(std::string t)
        :bar(std::move(t)){}
};

当你执行以下操作时:

int main() {
    foo f("some char*");
}

在第一种情况(完美转发)中,您只需使用const char*参数构造一个std::string。在第二种情况下,您构造了一个临时对象(从“some char*”得到t)和一个空的std::string对象,然后应用了一个移动操作。这并不是世界末日,但第一个版本简单地更有效率。
要完全清楚性能问题:
- 第一个版本使用1个分配 - 第二个版本使用1个分配和1个移动
而且,我所说的“移动”,并不是指std::move,因为它不会生成任何代码(它只是一个转换)。我所说的“移动”,指的是需要执行的代码,实际上是从字符串中移动的代码,它是std::string(std::string&&)的一部分。
再次强调 - 上面的例子是基于我在答案开头链接的讲话。它真的值得一看。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接