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?
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>;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With