I have a container-like class, that I would like to be move-only if the underlying type is move-only, but copyable otherwise. To make things simple, let's assume copyability is determined by a single bool template parameter:
template<bool IsCopyable>
struct Foo
{
Foo();
Foo(const Foo&); // only include this when IsCopyable is true
Foo(Foo&&);
Foo& operator=(const Foo&); // only when IsCopyable
Foo& operator=(Foo&&);
};
Now, I can't just SFINAE the copy ctor away, because that requires making it templated and a templated function can't be a copy ctor. Also, I can't just do a static_assert()
within the copy ctor. Although this will catch erroneous uses of the copy ctor, it also makes the class inherently copy constructible from the outside (the std::is_copy_constructible
type trait will yield true).
By the way, an unfortunate requirement is that it needs to compile in VC++ 2012, so I can't use fancy expression SFINAE, inheriting ctors, defaulted/deleted functions or constexpr if
(nevertheless, if you have a neat solution for C++17 I'd still like to hear it :))
The obvious method is to use template specialization. I'd rather not go this route, because in reality Foo has quite a lot of functionality and I don't like to repeat myself. Nevertheless, this seems my only option, and I can implement some code sharing using a base class, like so:
// Base functionality
template<bool IsCopyable>
struct FooBase
{
FooBase();
// move ctor and assignment can go here
FooBase(FooBase&&);
FooBase& operator=(FooBase&&);
// some generic conversion ctor and assignment that I happen to need
template<class T> FooBase(T&& t);
template<class T> FooBase& operator=(T&&);
// ... all sorts of functionality and datamembers
};
// Foo<false>
template<bool IsCopyable>
struct Foo : FooBase<IsCopyable>
{
// can't use inheriting ctors in VS 2012, wrap the calls manually:
Foo() { }
Foo(Foo&& other) : FooBase<IsCopyable>(std::move(other)) { }
Foo& operator=(Foo&& other)
{
FooBase<IsCopyable>::operator=(std::move(other));
return *this;
}
template<class T> Foo(T&& t) : FooBase<IsCopyable>(std::forward<T>(t)) { }
template<class T> Foo& operator=(T&& t)
{
FooBase<IsCopyable>::operator=(std::forward<T>(t));
return *this;
}
};
// Foo<true>
template<>
struct Foo<true> : FooBase<true>
{
// add these
Foo(const Foo&);
Foo& operator=(const Foo&);
// wrapping calls because of VS 2012:
Foo() { }
Foo(Foo&& other) : FooBase<true>(std::move(other)) { }
Foo& operator=(Foo&& other)
{
FooBase<true>::operator=(std::move(other));
return *this;
}
template<class T> Foo(T&& t) : FooBase<true>(std::forward<T>(t)) { }
template<class T> Foo& operator=(T&& t)
{
FooBase<true>::operator=(std::forward<T>(t));
return *this;
}
};
It's still a bit verbose. Fortunately it gets cleaner once you can use inheriting ctors and defaulted functions. Nevertheless, I was hoping there is a simpler way, ideally by not using a base class.
Can't you use a base class to selectively disable copying? That way you don't need to repeat any of the other functionality of the main class:
template <bool b>
struct MaybeCopyable {};
template <>
struct MaybeCopyable<false> {
MaybeCopyable(const MaybeCopyable&) = delete;
MaybeCopyable& operator=(const MaybeCopyable&) = delete;
};
template<bool IsCopyable>
struct Foo : MaybeCopyable<IsCopyable> {
// other functionality
};
If MaybeCopyable<false>
is one of the base classes, the copy constructor of Foo
will be automatically deleted.
struct nonesuch {
private:
~nonesuch();
nonesuch(const nonesuch&);
void operator=(const nonesuch&);
};
template<bool IsCopyable>
struct Foo {
Foo(const typename std::conditional<IsCopyable, Foo, nonesuch>::type& other) {
// copy ctor impl here
}
private:
Foo(const typename std::conditional<!IsCopyable, Foo, nonesuch>::type&);
};
Likewise for assignment.
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