类模板的可变构造函数模板的专业化

wow*_*bob 4 c++ templates variadic-templates c++11

这是一个具有可变构造函数的类,它是用于复制和从临时移动的特殊化.

template<class Obj>
class wrapper {
protected:
   Obj _Data;
public:

   wrapper(const wrapper<Obj>& w): _Data(w._Data) {}

   wrapper(wrapper<Obj>&& w):
      _Data(std::forward<Obj>(w._Data)) {}

   template<class ...Args>
   wrapper(Args&&... args):
      _Data(std::forward<Args>(args)...) {}

   inline Obj& operator()() { return _Data; }

   virtual ~wrapper() {}
};
Run Code Online (Sandbox Code Playgroud)

当我使用这样的专业化之一

wrapper<int> w1(9);
wrapper<int> w2(w1);
Run Code Online (Sandbox Code Playgroud)

我收到了错误:w1的类型被推断为w1.

VS2012的输出:

error C2440: 'initializing' : cannot convert from 'win::util::wrapper<int>' to 'int'
Run Code Online (Sandbox Code Playgroud)

如何解决这个问题呢?

Pra*_*ian 7

你被贪婪的完美转发构造函数所困扰.

wrapper<int> w2(w1);
Run Code Online (Sandbox Code Playgroud)

在上面的行中,与复制构造函数相比,完善转发构造函数更好地匹配,因为Args推导为wrapper<int>&.

快速解决方案是将上面的行更改为

wrapper<int> w2(static_cast<wrapper<int> const&>(w1));
Run Code Online (Sandbox Code Playgroud)

这正确地调用了复制构造函数,但除了不必要的冗长之外,并没有解决基本问题.

为了解决原来的问题,你需要禁用有条件完美转发构造函数时Args是一样的wrapper<Obj>.

这是一篇很好的博客文章,描述了这个问题,以及如何解决它.总而言之,您需要将完美的转发构造函数定义更改为

template <typename... Args,
          DisableIf<is_related<wrapper<Obj>, Args...>::value>...>
wrapper(Args&&... args):
    _Data(std::forward<Args>(args)...) {}
Run Code Online (Sandbox Code Playgroud)

在哪里is_related定义为

template <typename T, typename... U>
struct is_related : std::false_type {};

template <typename T, typename U>
struct is_related<T, U> : std::is_same<Bare<T>, Bare<U>> {};
Run Code Online (Sandbox Code Playgroud)

并且Bare

template <typename T>
using Bare = RemoveCv<RemoveReference<T>>;
Run Code Online (Sandbox Code Playgroud)

RemoveCvRemoveReference对于别名模板std::remove_cvstd::remove_reference分别.

现场演示