通过价值或通用参考

时间:2013-06-26 09:31:07

标签: c++ pass-by-value forwarding-reference pass-by-rvalue-reference

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

我们可以通过价值传递:

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不是类本身并且未复制类,则必须启用通用引用)。有任何想法吗?这两个版本都与我相同。

1 个答案:

答案 0 :(得分:5)

这些不是等效的,有时需要一个相对于另一个。 A stellar talk by 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*");
}

在第一种情况(完美转发)中,您只需使用std::string参数构造一个const char*。在第二种情况下,您将构造一个临时对象(来自t的{​​{1}}和一个空的"some char*"对象,然后应用一个移动操作。这不是世界末日,但第一个版本效率更高。

要绝对清楚性能:

  • 第一个版本使用 1个分配

  • 第二个版本使用 1个分配和1个移动

并且通过 move 并不是std::string,因为它不生成任何代码(只是强制转换)。 move 的意思是实际上需要从字符串移出的代码,该字符串是std::move 的一部分。

再次-上面的示例基于我在答案开头链接的谈话。 真的值得一看。