Home > Back-end >  Strange behavior in std::make_pair call with CRTP class
Strange behavior in std::make_pair call with CRTP class

Time:03-17

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:

  • 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

CodePudding user response:

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;
  • Related