右值引用和构造函数参数

Ada*_*ura 4 c++ constructor rvalue-reference c++11

让我们考虑一个简单的类

template< typename T >
class Wrapper {
public:
  // Constructors?
private:
  T wrapped;
};
Run Code Online (Sandbox Code Playgroud)

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


在C++ 0x之前,会有一个构造函数采用以下任一方法:

  1. const reference(T const&) - 如果type T为"heavy",
  2. 或value(T) - 如果type T为"light".

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

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

boost::call_traits通过允许类型创建者将类型标记为"轻"(通过提供适当的call_traits专业化),库提供了一些克服此困难的方法.否则它将被视为"沉重".似乎可以接受.


但是C++ 0x使它变得更糟.因为现在你还有rvalue reference(T&&),它允许有效地获取(某些)"重"对象.

因此,你必须选择:

  1. 只是const reference(T const&) - 如果类型T是"重"并且不支持移动语义(因为没有任何一个 - 就像大POD一样 - 或者没有写入并且你对它没有影响),
  2. const reference(T const&)和rvalue reference(T&&) - 如果type T为"heavy"并且支持移动语义,
  3. 只是value(T) - 如果type T是"light"或者它是"重"但支持移动语义(即使复制它也不会使用,否则我们无论如何都要从T const&自己复制......).

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


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

这个问题有什么解决方案吗?

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

Ker*_* SB 7

相反,由于通用引用,C++ 11使其变得更容易:

template <typename T> struct Wrapper
{
    T value;

    template <typename U> Wrapper(U && u)
    : value(std::forward<U>(u))
    {  }
};
Run Code Online (Sandbox Code Playgroud)

作为一个特别好的触摸,你应该添加一个默认的第二个参数,该参数只有在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)...)
{  }
Run Code Online (Sandbox Code Playgroud)

确保#include <utility>为特征forward#include <type_traits>特征.