4

让我们考虑一个简单的类

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

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


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

  1. const reference ( T const&) - 如果类型T是“重”,
  2. 或 value ( T) - 如果 typeT是“light”。

确定类型T是“重”还是“轻”并不容易。

可以假设只有内置类型(ints/floats/...)是“轻量级”的。但这并不完全正确,因为我们Wrapper<int>最有可能也应该将其视为“轻”类型。

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


但是 C++0x 使情况变得更糟。因为现在您还有右值引用 ( T&&),它允许有效地获取(一些)“重”对象。

正因为如此,现在您必须选择:

  1. 只是 const reference ( T const&) - 如果类型T是“重”并且不支持移动语义(因为要么没有 - 就像大型 POD - 或者没有写入并且你对此没有影响),
  2. const 引用 ( T const&) 和 rvalue 引用 ( T&&) - 如果类型T是“重”并且确实支持移动语义,
  3. just value ( T) - 如果类型T是“轻”或“重”但支持移动语义(即使复制了它也不会打扰使用,否则我们无论如何都必须从T const&自己复制......)。

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


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

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

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

4

2 回答 2

7

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

template <typename T> struct Wrapper
{
    T value;

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

T作为一个额外的好处,您应该添加一个默认的第二个参数,该参数仅在可构造 from时才存在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>为特征。

于 2012-07-20T11:26:52.367 回答
3

如果您T无论如何都要复制,最好按值传递参数并让编译器找出复制。不管你做什么,至少会有一份副本。

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

想要速度?戴夫亚伯拉罕的价值传递。

于 2012-07-20T12:46:17.357 回答