Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Strange behavior in std::make_pair call with CRTP class

Problem

I have a simple CRTP-pattern class BaseInterface, and two classes, derived from this class: test_dint and test_dint2.

Difference between test_dint and test_dint2 – in test_dint dtor explicitly declared as ~test_dint() = default;.

I’m try make std::pair with types <std::intptr_t, test_dint> by calling std::make_pair and compilation is failed with error:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

  • MSVC – error C2440: '<function-style-cast>': cannot convert from 'initializer list' to '_Mypair'
  • CLang 11 – error: no matching constructor for initialization of '__pair_type' (aka 'pair<long, test_dint>')

But, if types in pair change to <std::intptr_t, test_dint2> – all compiles without errors.

I can’t understand – why does explicit dtor declaration change behavior of std::pair template?

Full code

#include <memory>
#include <unordered_map>

template<typename DerivedT>
class enable_down_cast
{  
public:
    DerivedT const& impl() const
    {
        // casting "down" the inheritance hierarchy
        return *static_cast<DerivedT const*>(this);
    }

    DerivedT& impl()
    {
        return *static_cast<DerivedT*>(this);
    }

    //~enable_down_cast() = default;

protected:
    // disable deletion of Derived* through Base*
    // enable deletion of Base* through Derived*
    ~enable_down_cast() = default;

private:  
    using Base = enable_down_cast;  
};

template<typename Impl>
class BaseInterface : public enable_down_cast<Impl>
{
public:
    using handle_type = std::intptr_t;

    BaseInterface() = default;

    // Disable copy
    BaseInterface(const BaseInterface&) = delete;
    BaseInterface& operator=(const BaseInterface&) = delete;

    // Enable move
    BaseInterface(BaseInterface&&) = default;
    BaseInterface& operator=(BaseInterface&&) = default;

    ~BaseInterface() = default;

    handle_type handle() const
    {
        return m_handle;
    }

protected:
    handle_type m_handle{ 0 };

private:
    using enable_down_cast<Impl>::impl;
};

class test_dint : public BaseInterface<test_dint> {
public:
    test_dint() = delete;
    test_dint(const handle_type handle) : 
        BaseInterface<test_dint>()
    {
        m_handle = handle;
    }
    ~test_dint() = default;
};

class test_dint2 : public BaseInterface<test_dint2> {
public:
    test_dint2() = delete;
    test_dint2(const handle_type handle) : 
        BaseInterface<test_dint2>()
    {
        m_handle = handle;
    }
};


int main()
{
    test_dint::handle_type handle = 100500;
    std::make_pair(handle, test_dint{ handle }); // <--- failed ctor
    std::make_pair(handle, test_dint2{ handle });
    return 0;
}

Live demo

https://godbolt.org/z/eee7h47v7

>Solution :

It’s because when you declared the destructor, you prevent the compiler from generate a move constructor, so test_dint is not moveconstructable (nor copyconstructable since it’s base) anymore.


explicitly declare it would make it work.

test_dint(test_dint&&)=default;
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading