使用 CRTP 类进行 std::make_pair 调用时的奇怪行为
问题
我有一个简单的 CRTP 模式类 BaseInterface
,以及从该类派生的两个类:test_dint
和 test_dint2
。
test_dint
和 test_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;
}
现场演示
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这是因为当您声明析构函数时,您阻止编译器生成移动构造函数,因此
test_dint
不再是可移动构造的(也不再是可复制构造的,因为它是基础的)。明确声明它会起作用。
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.