代码之家  ›  专栏  ›  技术社区  ›  arynaq

无拷贝构造函数的简单编译时检查

  •  5
  • arynaq  · 技术社区  · 7 年前

    struct NoCopy {
       NoCopy(){}
       NoCopy(const NoCopy&) = delete;
       NoCopy& operator=(const NoCopy&) = delete;  
    };
    

    struct Foo : NoCopy {
        Foo(){}
    };
    

    这将使下面的代码无法编译

    Foo f;
    Foo f2 = f;
    

    struct Foo2 : NoCopy {
        Foo2(){}
        Foo2(const Foo2&){}
    };
    

    这是完全合法的,但毫无意义,我现在有一个类型,既可复制,也不可复制(通过其基类)。

    3 回复  |  直到 7 年前
        1
  •  8
  •   StoryTeller - Unslander Monica    7 年前

    这是C++。在模板元编程的世界里,几乎任何事情都是可能的。如果我们 NoCopy 基于CRTP,我们可以在其析构函数中添加静态断言。

    template<class C>
    struct NoCopy {
       NoCopy(){}
       NoCopy(const NoCopy&) = delete;
       NoCopy& operator=(const NoCopy&) = delete;
       ~NoCopy() noexcept {
           static_assert(std::is_base_of<NoCopy, C>::value, "CRTP not observed");
           static_assert(!std::is_copy_constructible<C>::value, "A non-copyable copyable class? Really?");
       }
    };
    

    这是你的代码,适合 live example .


    经过进一步思考,如果只提供初始化类的单一方法,则默认构造函数

    template<class C>
    struct NoCopy {
       NoCopy() noexcept {
           static_assert(std::is_base_of<NoCopy, C>::value, "CRTP not observed");
           static_assert(!std::is_copy_constructible<C>::value, "A non-copyable copyable class? Really?");
       }
       NoCopy(const NoCopy&) = delete;
       NoCopy& operator=(const NoCopy&) = delete;
    };  
    

    this live example

        2
  •  3
  •   Max Langhof    7 年前

    如果你的目的是为了防止人们不小心忘记打电话 NoCopy 在自己的复制构造函数中(禁止命名)复制构造函数,我建议如下:

    namespace
    {
        struct NotCopyableInitT {};
    }    
    
    // You can choose whatever stern words you want here.
    NotCopyableInitT initNoCopy() { return {}; }
    
    struct NoCopy {
        explicit NoCopy(NotCopyableInitT){}
        NoCopy(const NoCopy&) = delete;
        NoCopy& operator=(const NoCopy&) = delete;  
    };
    

    如果他们坚持在禁止复制的地方添加可复制性,你会强迫他们说出自己的错误:

    struct Foo2 : mylib::NoCopy {
        Foo2() : NoCopy(mylib::initNoCopy()) {}
        // Users have to spell out this line in order to get a copy constructor.
        // That certainly goes beyond being forgetful.
        Foo2(const Foo2&) : NoCopy(mylib::initNoCopy()) {}
    };
    

    Demo

    对于行为良好的用户,它只是 夜视

        3
  •  1
  •   YSC    7 年前

    你可以简单地 检查

    #include <type_traits>
    
    namespace lib {
    template<class NoCopy>
    inline constexpr bool copiable_v = std::disjunction<
        std::is_copy_constructible<NoCopy>,
        std::is_copy_assignable<NoCopy>
    >::value;
    
    template<class NoCopy>
    struct CheckNoCopiable
    {
        static_assert(
            copiable_v<NoCopy> == false,
            "Type is copy-assignable or copy-constructible."
        );
    };
    }
    

    使用该工具,您的函数可以如下所示:

    namespace lib {
    template<class NoCopy>
    void f(NoCopy&& nc)
    {
        CheckNoCopiable<NoCopy>{};
    
        /* do whatever with nc */
    }
    }
    

    http://coliru.stacked-crooked.com/a/ed0a8f5576a68554 :

    struct Alice   : lib::NoCopy {}; // nice Alice
    struct Bob                   {}; // nice Bob
    struct Charlie : lib::NoCopy     // naughty Charly
    {
        Charlie()               {};
        Charlie(Charlie const&) {};
        Charlie& operator=(const Charlie&) { return *this; }; 
    };
    
    int main()
    {
       lib::f(Alice{});
       //lib::f(Bob{});     // error: static assertion failed: Type is copy-assignable or copy-constructible.
       //lib::f(Charlie{}); // error: static assertion failed: Type is copy-assignable or copy-constructible.
    }