Why are mutexes and condition variables trivially copyable?

后端 未结 3 1187
野的像风
野的像风 2021-02-13 20:34

LWG 2424 discusses the undesirable status of atomics, mutexes and condition variables as trivially copyable in C++14. I appreciate that a fix is already lined up, but std::mutex

3条回答
  •  南方客
    南方客 (楼主)
    2021-02-13 21:17

    Either it was my mistake, or I was misquoted, and I honestly don't recall which.

    However, I have this very strongly held advice on the subject:

    Do not use is_trivial nor is_trivially_copyable! EVER!!!

    Instead use one of these:

    is_trivially_destructible
    is_trivially_default_constructible
    is_trivially_copy_constructible
    is_trivially_copy_assignable
    is_trivially_move_constructible
    is_trivially_move_assignable
    

    Rationale:

    tldr: See this excellent question and correct answer.

    No one (including myself) can remember the definition of is_trivial and is_trivially_copyable. And if you do happen to look it up, and then spend 10 minutes analyzing it, it may or may not do what you intuitively think it does. And if you manage to analyze it correctly, the CWG may well change its definition with little or no notice and invalidate your code.

    Using is_trivial and is_trivially_copyable is playing with fire.

    However these:

    is_trivially_destructible
    is_trivially_default_constructible
    is_trivially_copy_constructible
    is_trivially_copy_assignable
    is_trivially_move_constructible
    is_trivially_move_assignable
    

    do exactly what they sound like they do, and are not likely to ever have their definition changed. It may seem overly verbose to have to deal with each of the special members individually. But it will pay off in the stability/reliability of your code. And if you must, package these individual traits up into a custom trait.

    Update

    For example, clang & gcc compile this program:

    #include 
    
    template 
    void
    test()
    {
        using namespace std;
        static_assert(!is_trivial{}, "");
        static_assert( is_trivially_copyable{}, "");
        static_assert( is_trivially_destructible{}, "");
        static_assert( is_destructible{}, "");
        static_assert(!is_trivially_default_constructible{}, "");
        static_assert(!is_trivially_copy_constructible{}, "");
        static_assert( is_trivially_copy_assignable{}, "");
        static_assert(!is_trivially_move_constructible{}, "");
        static_assert( is_trivially_move_assignable{}, "");
    }
    
    struct X
    {
        X(const X&) = delete;
    };
    
    int
    main()
    {
        test();
    }
    

    Note that X is trivially copyable, but not trivially copy constructible. To the best of my knowledge, this is conforming behavior.

    VS-2015 currently says that X is neither trivially copyable nor trivially copy constructible. I believe this is wrong according to the current spec, but it sure matches what my common sense tells me.

    If I needed to memcpy to uninitialized memory, I would trust is_trivially_copy_constructible over is_trivially_copyable to assure me that such an operation would be ok. If I wanted to memcpy to initialized memory, I would check is_trivially_copy_assignable.

提交回复
热议问题