Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enable Copy Constructor if function is found

I have a RAII wrapper around a Foo* where Foo is a library.

The class looks pretty much like this:

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

Error handling and assignment operators omitted for brevity.

The problem now is, that foo_clone is available in 1 library version, but not an earlier one and the wrapper should support both. There is no *_VERSION macro for me to check so I need to do it in C++ instead of the preprocessor.

I though of something like:

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

But this doesnt work: The defined move-ctor requires me to add a copy ctor verbatim as Handle(const Handle&), no template trickery seems to be allowed as otherwise the compiler considers the copy-ctor as implicitely deleted.

What can I do?

like image 606
Flamefire Avatar asked Mar 30 '26 00:03

Flamefire


1 Answers

You cannot indeed SFINAE that special member which should not be template.

So you can template the class itself:

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

In C++20, you might do a little better thanks to requires to "discard" methods:

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>;
like image 200
Jarod42 Avatar answered Apr 02 '26 04:04

Jarod42