使用 CRTP 类进行 std::make_pair 调用时的奇怪行为

发布于 2025-01-14 12:27:06 字数 2900 浏览 3 评论 0原文

问题

我有一个简单的 CRTP 模式类 BaseInterface,以及从该类派生的两个类:test_dinttest_dint2

test_dinttest_dint2 之间的区别 - 在 test_dint dtor 中显式声明为 ~test_dint() = default;

我尝试通过调用 std::make_pair 来创建类型为 的 std::pair ,编译失败并出现错误:

  • MSVC - error C2440: '< ;function-style-cast>': 无法从 'initializer list' 转换为 '_Mypair'
  • CLang 11 - 错误:没有匹配的初始化构造函数'__pair_type' (aka 'pair')

但是,如果对中的类型更改为 - 所有编译都不会出现错误。

我无法理解 - 为什么显式 dtor 声明会改变 std::pair 模板的行为?

完整代码

#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;
}

现场演示

https://godbolt.org/z/eee7h47v7

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

请帮我爱他 2025-01-21 12:27:06

这是因为当您声明析构函数时,您阻止编译器生成移动构造函数,因此 test_dint 不再是可移动构造的(也不再是可复制构造的,因为它是基础的)。


明确声明它会起作用。

test_dint(test_dint&&)=default;

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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文