【问题标题】:Enable Copy Constructor if function is found如果找到函数,则启用复制构造函数
【发布时间】:2019-08-01 18:57:40
【问题描述】:

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

这个类看起来很像这样:

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&amp;),似乎不允许使用模板欺骗,否则编译器会认为 copy-ctor 被隐式​​删除。

我能做什么?

【问题讨论】:

  • @Paulo1205:OP 的主要问题是针对无法模板化的 SFINAE 特殊成员。即使 OP 的检测代码不正确。
  • 您确定代码不正确? foo_clone(如果存在)返回一个指针,因此此方法类似于 template&lt;T, std::enable_if_t&lt;...&gt;* = nullptr&gt; 对于布尔解决方案,当然必须使用另一种方法。
  • @Flamefire:至少有:std::declval&lt;T&gt;::p -> std::declval&lt;T&gt;().p。可能被视为错字或您的问题。
  • 谢谢,已修复。在修改 SO 的代码时确实是一个错字

标签: c++ constructor sfinae


【解决方案1】:

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

所以你可以模板类本身:

// 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 “丢弃”方法,您可能会做得更好:

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>;

【讨论】:

  • 我猜到了这样的事情,但想避免必须两次实现大部分内容。还有作业,p当然是私有的,所以有getter……我想连C++20的概念都解决不了吧? (反正我需要 C++14,但很好奇)
  • C++20 允许一些简化。在 C++20 之前,您可以通过使用基类 HandleImpl(带有专门化)来处理 protected Foo* p(构造函数/析构函数/复制/赋值)和派生类来处理额外方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-10-13
  • 1970-01-01
  • 2013-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-07
相关资源
最近更新 更多