Search code examples
c++type-traits

is_copy_constructible typetrait gives wrong answer


In the below code, is_copy_constructible_v returns true, but MainClass' copy constructor is marked as deleted.

Trying to call the copy constructor is be a compilation error.
So how come that is_copy_constructible_v does not see it?

class BaseClass {};

class MainClass : public BaseClass {
public:
    MainClass(MainClass&) = delete;

    MainClass(const BaseClass& rhs)
    {
    }
};

int main()
{
    MainClass first{{}};

    //This line will not compile because MainClass::MainClass(MainClass&) is a deleted function
    //MainClass second{first};

    auto mainclass_is_constructible = std::is_copy_constructible_v<MainClass>;

    //This assert fails because is_copy_constructible_v returns true
    static_assert((std::is_copy_constructible_v<MainClass>) == 0);
}

This was found from having unit tests for that check if a given class is copyable/movable.
They work fine, but not after adding the equivalent of MainClass(const BaseClass& rhs) in real code.

If instead we have MainClass(const BaseClass& rhs, int value), then is_copy_constructible_v will work.

What would happen given an std::vector<MainClass>, if we also delete the move ctor/assignment? Won't the vector make use of is_copy_constructible_v to detect how to operate best, and receive a wrong answer?

Many thanks


Solution

  • MainClass has a MainClass::MainClass(const BaseClass& rhs), which makes constructing MainClass from a const lvalue possible, e.g.

    const MainClass first{{}};
    
    // This line will compile because MainClass::MainClass(const BaseClass& rhs) is usable
    // a MainClass could be bound to const BaseClass&
    MainClass second{first};
    

    And std::is_copy_constructible performs the check as:

    provides a member constant value equal to std::is_constructible<T, const T&>::value.

    Note that it's checking whether T could be constructed from const T&, then it yields true for MainClass.

    If you declare the copy constructor as:

    MainClass(const MainClass&) = delete;
    

    You'll get the result false.

    What would happen given an std::vector<MainClass>

    It'll use the MainClass::MainClass(const BaseClass& rhs) (for push_back or reallocation etc). If it's not what you expect you have to reconsider the design.