右值引用和构造函数参数

rvalue references and constructor arguments

本文关键字:构造函数 参数 引用      更新时间:2023-10-16

让我们考虑一个简单的类

template< typename T >
class Wrapper {
public:
  // Constructors?
private:
  T wrapped;
};

它应该使用什么构造函数才能有效?


在 C++0x 之前,将有一个构造函数,它采用:

  1. 常量引用 ( T const& ( - 如果类型 T 是 "重",
  2. 或值 ( T ( - 如果类型 T 是 "light"。

确定T型是"重"还是"轻"并不容易。

人们可以假设只有内置类型(ints/floats/...(是"轻量级"的。但这并不完全正确,因为我们自己的Wrapper<int>很可能也应该被视为"轻型"类型。

boost::call_traits这样的库提供了一些方法来克服这个困难,允许类型创建者将类型标记为"轻量级"(通过提供适当的call_traits专用化(。否则将被视为"重"。似乎可以接受。


但是C++0x使情况变得更糟。因为现在你也有右值引用(T&&(,它允许有效地采取(一些("重"物体。

正因为如此,现在你必须在以下方面做出选择:

  1. 只是常量引用(T const&( - 如果类型 T 是"重"并且不支持移动语义(因为要么没有 - 就像大型 POD 一样 - 要么没有编写并且您对此没有影响(,
  2. 常量引用(T const&(和右值引用(T&&( - 如果类型T是"重"并且确实支持移动语义,
  3. 只是值 ( T ( - 如果类型 T 是"轻"的,或者如果它是"重的"但支持移动语义(即使进行了复制,它也不会打扰使用,因为否则我们将不得不从T const&复制自己......

仍然不容易分辨哪些类型是"重的",哪些是"轻的"(如前所述(。但是现在你也无法判断类型 T 是否支持移动语义(或者你是?


一旦包装多个值,这变得更加烦人,因为可能的构造函数重载的数量呈指数级增长。

这个问题有什么解决办法吗?

我虽然有一些用于转发(完美转发(参数的模板构造函数,但我不确定这是否会按预期工作。它还允许提供不同类型的值,这些值将转发给构造函数T。这可能被视为一项功能,但并非必须如此。

相反,由于通用引用,C++11 使它更容易

template <typename T> struct Wrapper
{
    T value;
    template <typename U> Wrapper(U && u)
    : value(std::forward<U>(u))
    {  }
};

作为一个额外的好处,你应该添加一个默认的第二个参数,该参数只存在于T可以从U构造时,这样就不会让你的类本身看起来可以从不匹配的类型构造。并使其变频:

template <typename ...Args>
Wrapper(Args &&... args,
        typename std::enable_if<std::is_constructible<T, Args...>::value, int>::type = 0)
: value(std::forward<Args>(args)...)
{  }

确保#include <utility> forward#include <type_traits>特征。

如果你无论如何都要复制你的T,最好按值传递参数,让编译器弄清楚复制。无论你做什么,无论如何都会有一个副本。

template< typename T >
class Wrapper {
public:
  Wrapper(T value) : wrapped(std::move(value))
  { }
private:
  T wrapped;
};

请参阅想要速度?通过戴夫·亚伯拉罕斯的值。