Add copy constructor based on template options

I have a container-like class that I would like to use only for moving, if the base type is only for moving, but otherwise it could be copied. To make things simpler, suppose that the ability to copy is specified by one parameter of the bool template:

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 SEND a copy of ctor, because it requires creating a template, and the template function cannot be a copy of ctor. Also, I cannot just do static_assert() inside a copy of ctor. Although this will lead to an erroneous use of the ctor copy, it also forces the class to inherently copy the construct from the outside (a dash of type std::is_copy_constructible will give true).

By the way, the unfortunate requirement is that it must compile in VC ++ 2012, so I cannot use the fancy SFINAE expression, ctors inheritance, default / deleted functions, or constexpr if (however, if you have a neat solution for C ++ 17 I still would like to hear it :))

The obvious method is to use specialized specialization. I would prefer not to go this route, because in fact Foo has quite a lot of functionality, and I do not like to repeat myself. However, this is my only option, and I can implement some code exchange using the base class, for example:

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

This is still a bit verbose. Fortunately, it gets cleaner if you can use ctors inheritance and default functions. However, I was hoping there was an easier way, ideally, not using a base class.

+7
c ++ c ++ 11 templates sfinae
source share
3 answers
 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&); }; 

Similarly for the destination.

+9
source share

Can you use the base class to selectively disable copying? Thus, you do not need to repeat any other functions 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 Foo will be automatically deleted.

+3
source share

I don’t know if it can work with your VC ++ 2012, but ... if I remember correctly, there is a method that I think is slightly better than inheriting from FooBase .

Type of car inheritance.

You can declare a struct

 template <bool> struct Bar; 

and specialize true version with all constructors / operators

 template <> struct Bar<true> { Bar () { std::cout << "Bar ()" << std::endl; }; Bar (Bar &&) { std::cout << "Bar (Bar &&)" << std::endl; } Bar (Bar const &) { std::cout << "Bar (Bar const &)" << std::endl; } Bar & operator= (Bar &&) { std::cout << "operator= (Bar &&)" << std::endl; return *this; } Bar & operator= (Bar const &) { std::cout << "operator= (Bar const &)" << std::endl; return *this; } }; 

Now you can specialize the false version, inheriting from Bar<true> , confirming ( = default ) what you want and deleting ( = delete ) what you don't want; something like

 template <> struct Bar<false> : public Bar<true> { using Bar<true>::Bar; // confirmed constructor/operators Bar () = default; Bar (Bar &&) = default; Bar & operator= (Bar &&) = default; // deleted constructor/operators Bar (Bar const &) = delete; Bar & operator= (Bar const &) = delete; }; 

Below is a complete working example

 #include <iostream> template <bool> struct Bar; template <> struct Bar<true> { Bar () { std::cout << "Bar ()" << std::endl; }; Bar (Bar &&) { std::cout << "Bar (Bar &&)" << std::endl; } Bar (Bar const &) { std::cout << "Bar (Bar const &)" << std::endl; } Bar & operator= (Bar &&) { std::cout << "operator= (Bar &&)" << std::endl; return *this; } Bar & operator= (Bar const &) { std::cout << "operator= (Bar const &)" << std::endl; return *this; } }; template <> struct Bar<false> : public Bar<true> { using Bar<true>::Bar; // confirmed constructor/operators Bar () = default; Bar (Bar &&) = default; Bar & operator= (Bar &&) = default; // deleted constructor/operators Bar (Bar const &) = delete; Bar & operator= (Bar const &) = delete; }; int main () { Bar<true> bt1; Bar<false> bf1; auto bt2 = bt1; // auto bf2 = bf1; compilation error (copy constructor deleted) auto bt3 = std::move(bt1); auto bf3 = std::move(bf1); Bar<true> bt4; Bar<false> bf4; bt4 = bt3; // bf4 = bf3; compilation error (operator= (Bar const &) deleted) bt3 = std::move(bt4); bf3 = std::move(bf4); } 
0
source share

All Articles