如果找到函数,则启用复制构造函数

Enable Copy Constructor if function is found

我有一个围绕 Foo* 的 RAII 包装器,其中 Foo 是一个库。

class 看起来很像:

struct Handle{
  Foo* p;
  Handle(): p(foo_new()){}
  Handle(Handle&& f): p(std::exchange(f.p, nullptr)){}
  Handle(const Handle& f){
    p = foo_clone(f.p);
  }
};

为简洁起见,省略了错误处理和赋值运算符。

现在的问题是,foo_clone 在 1 个库版本中可用,但在较早的版本中不可用,并且包装器应该支持两者。没有 *_VERSION 宏可供我检查,所以我需要用 C++ 而不是预处理器来检查。

我想到了类似的东西:

template<class T, decltype(foo_clone(std::declval<T>().p)) = nullptr>
Handle(const T& f){...}

但这不起作用:定义的 move-ctor 要求我逐字添加一个复制 ctor 作为 Handle(const Handle&),似乎不允许模板欺骗,否则编译器认为复制 ctor 被隐式​​删除。

我能做什么?

你确实不能SFINAE那个不应该是模板的特殊成员。

因此您可以模板 class 本身:

// I let you implement traits has_foo_clone<T>


template <typename T, bool = has_foo_clone<T>::value>
struct HandleImpl
{
  T* p;
  HandleImpl(): p(foo_new()) {}
  HandleImpl(HandleImpl&& f): p(std::exchange(f.p, nullptr)){}
  HandleImpl(const HandleImpl& f){ p = foo_clone(f.p); }
};

template <typename T>
struct HandleImpl<T, false>
{
    T* p;
    HandleImpl(): p(foo_new()) {}
    HandleImpl(HandleImpl&& f): p(std::exchange(f.p, nullptr)){}
    HandleImpl(const HandleImpl& f) = delete;
};

using Handle = HandleImpl<Foo>;

在 C++20 中,由于 requires 到 "discard" 方法,您可能会做得更好:

template <typename T>
struct HandleImpl
{
  T* p;
  HandleImpl(): p(foo_new()) {}
  HandleImpl(HandleImpl&& f): p(std::exchange(f.p, nullptr)){}
  HandleImpl(const HandleImpl& f) requires(has_foo_clone<T>::value) { p = foo_clone(f.p); }
};

using Handle = HandleImpl<Foo>;