Conditionally disabling a copy constructor

后端 未结 6 1654
眼角桃花
眼角桃花 2020-12-08 04:34

Suppose I\'m writing a class template C that holds a T value, so C can be copyable only if T is copyabl

相关标签:
6条回答
  • 2020-12-08 04:52

    This is a bit of a trick, but it works.

    template<bool b,class T>
    struct block_if_helper{
      using type=T;
    };
    template<class T>
    struct block_if_helper<true, T>{
      class type{
        type()=delete;
      };
    };
    template<bool b,classT>
    using block_if=typename block_if_helper<b,T>::type;
    template<bool b,classT>
    using block_unless=typename block_if_helper<!b,T>::type;
    

    now we create a method that is your copy ctor ... maybe.

    template<class X>
    struct example {
      enum { can_copy = std::is_same<X,int>{} };
    
      example( block_unless<can_copy, example>const& o ); // implement this as if `o` was an `example`
      // = default not allowed
      example( block_if<can_copy, example>const& )=delete;
    };
    

    and now the =default is the copy ctor if and only if can_copy, and the =delete of not. The stub type that it is otherwise cannot be created.

    I find this technique useful for general method disabling on compilers that do not support the default template argument feature, or for methods (like virtual or special) that cannot be templates.

    0 讨论(0)
  • 2020-12-08 05:04

    However, it's not always possible to structure your class so that defaulted constructors will do the right thing.

    It's usually possible with enough effort.

    Delegate the work that can't be done by a defaulted constructor to another member, or wrap the T member in some wrapper that does the copying, or move it into a base class that defines the relevant operations.

    Then you can define the copy constructor as:

      C(const C&) = default;
    

    Another way to get the compiler to decide whether the default definition should be deleted or not is via a base class:

    template<bool copyable>
    struct copyable_characteristic { };
    
    template<>
    struct copyable_characteristic<false> {
      copyable_characteristic() = default;
      copyable_characteristic(const copyable_characteristic&) = delete;
    };
    
    template <typename T>
    class C
    : copyable_characteristic<std::is_copy_constructible<T>::value>
    {
     public:
      C(const C&) = default;
      C(C&& rhs);
    
      // other stuff
    };
    

    This can be used to delete operations using arbitrary conditions, such as is_nothrow_copy_constructible rather than just a straightforward T is copyable implies C is copyable rule.

    0 讨论(0)
  • 2020-12-08 05:07

    C::C(C const& rhs, std::enable_if<true, int>::type dummy = 0) is also a copy ctor because the second argument has a default value.

    0 讨论(0)
  • 2020-12-08 05:09

    A noteworthy approach is partial specialization of the surrounding class template.

    template <typename T,
              bool = std::is_copy_constructible<T>::value>
    struct Foo
    {
        T t;
    
        Foo() { /* ... */ }
        Foo(Foo const& other) : t(other.t) { /* ... */ }
    };
    
    template <typename T>
    struct Foo<T, false> : Foo<T, true>
    {
        using Foo<T, true>::Foo;
    
        // Now delete the copy constructor for this specialization:
        Foo(Foo const&) = delete;
    
        // These definitions adapt to what is provided in Foo<T, true>:
        Foo(Foo&&) = default;
        Foo& operator=(Foo&&) = default;
        Foo& operator=(Foo const&) = default;
    };
    

    This way the trait is_copy_constructible is satisfied exactly where T is_copy_constructible.

    0 讨论(0)
  • 2020-12-08 05:12

    If you want to conditionally disable your copy constructor, you definitely want it to participate in overload resolution - because you want it to be a loud compile error if you try to copy it.

    And to do that, all you need is static_assert:

    template <typename T>
    class C {
    public:
        C(const C& rhs) {
            static_assert(some_requirement_on<T>::value, 
                "copying not supported for T");
        }
    };
    

    This will allow copy construction only if some_requirement_on<T> is true, and if it's false, you can still use the rest of the class... just not copy construction. And if you do, you'll get a compile error pointing to this line.

    Here's a simple example:

    template <typename T>
    struct Foo
    {
        Foo() { }
    
        Foo(const Foo& ) {
            static_assert(std::is_integral<T>::value, "");
        }
    
        void print() {
            std::cout << "Hi" << std::endl;
        }
    };
    
    int main() {
        Foo<int> f;
        Foo<int> g(f); // OK, satisfies our condition
        g.print();     // prints Hi
    
        Foo<std::string> h;
        //Foo<std::string> j(h); // this line will not compile
        h.print(); // prints Hi
    }
    
    0 讨论(0)
  • 2020-12-08 05:15
    template <typename T>
    class variant {
        struct moo {};
    public:
      variant(const variant& ) = default;
      variant(std::conditional_t<!std::is_copy_constructible<T>::value,
                                 const variant&, moo>,
              moo=moo());
      variant() {};
    };
    

    This makes a non-eligible template instance have two copy constructors, which makes it not copy constructible.

    0 讨论(0)
提交回复
热议问题