10

I want to develop a small polymorphic class with type erasure and I wonder which version of the templatized constructor is better and should be used.

We can pass by value:

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

or we can use a universal reference:

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

(The universal reference has to be enabled if for the case that T is not the class itself and the class is not copied). Any ideas? Both version look equal to me.

4

1 回答 1

8

这些是不等价的,有时需要一个而不是另一个。Nicolai Josuttis 的精彩演讲值得一个小时来讨论这个问题。我强烈建议至少看一次。

就个人而言,除非您遇到特殊情况,即转换成本高昂并且您希望尽可能避免临时使用,否则我建议您只传递值并std::moveing 参数。

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::stringwithconst char*参数。在第二种情况下,您构造一个临时(tfrom "some char*")和一个空std::string对象,然后应用一个移动操作。这不是世界末日,但第一个版本更有效。

要绝对清楚性能:

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

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

并且我的意思不是移动std::move,因为它不生成任何代码(它只是一个演员表)。移动我的意思是需要执行的代码实际上是从字符串中移动的,它是std::string(std::string&&).

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

于 2018-09-11T13:07:48.257 回答