文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

C++类的特种函数生成机制详解

2024-04-02 19:55

关注

C++类的特种函数生成机制

规则

参考Effective Morder C++上的说明:

例子:A BUG

因为不熟悉析构函数的生成机制,导致了一个BUG。

首先,下面的代码没有问题,因为数据成员m_,所以Widget默认也是个只移型别;mm中也可以插入一个由只移型别构造的std::pair<int, Widget>,因为pair默认支持右值参数构造(可以由只移的Widget构造)和自身的移动构造函数(可以移动构造到unordered_map中):


class Widget {
public:
    Widget() = default;
//    ~Widget() = default;
private:
    std::thread m_; // 只移型别
};
unordered_map<int, Widget> mm;
mm.insert({12, Widget()});

然后,我手贱加了一个默认的析构函数:


class Widget {
public:
    Widget() = default;
    ~Widget() = default;
private:
    std::thread m_; // 只移型别
};
unordered_map<int, Widget> mm;
mm.insert({12, Widget()}); // error!

报错信息极长,核心错误是:


error: no matching function for call to ‘std::unordered_map<int, Widget>::insert(<brace-enclosed initializer list>)'
   45 |     unordered_map<int, Widget> mm;

可以把std::pair的构造单独抽出来看到更清晰的报错信息:


// 代码如下:
make_pair(12, Widget());
// 报错如下:
In template: no matching constructor for initialization of '__pair_type' (aka 'pair<int, Widget>')

“显然”,是因为Widget的移动构造函数被隐式删除了(它既不能拷贝也不能移动了),所以无法由Widget参数构造一个std::pair。

解决方案就是不要定义析构函数,或者显式定义一个移动构造函数:


class Widget {
public:
    Widget() = default;
    Widget(Widget&&) = default;
    ~Widget() = default;
private:
    std::thread m_; // 只移型别
};
unordered_map<int, Widget> mm;
mm.insert({12, Widget()});

例子:std::mutex和std::thread

在我做试验的时候,一开始错把std::mutex记成了只移型别

定义了一个这样的类:


class Widget {
public:
    Widget() = default;
private:
    std::mutex m_;
};
unordered_map<int, Widget> mm;
mm.insert({12, Widget()}); // error!

甚至在我没有添加析构函数的时候Widget就不能拷贝和移动了。

看看源码:


class mutex : private __mutex_base
{
  public:
    
    mutex() noexcept = default;
    ~mutex() = default;
    mutex(const mutex&) = delete;
    mutex& operator=(const mutex&) = delete;
	  
}

显然,因为mutex自行定义了默认的析构函数而且把拷贝构造函数定义为删除的,那么它的移动构造函数也会被隐式删除,所以mutex既不能拷贝也不能移动。

和std::thread源码比较一下:


class thread
{
  public:
  thread() noexcept = default;
  thread(const thread&) = delete;
  thread(thread&& __t) noexcept
  { 
    swap(__t);
  }
  ~thread()
  {
    if (joinable())
      std::terminate();
  }
}

虽然std::thread定义了析构函数和删除的拷贝构造函数,但是它显式定义了移动构造函数,这使得它虽然不能拷贝但是可以移动。

题外话:为什么std::mutex不可移动?

大体来说就是std::mutex一般由多个线程调用,那么如果它的位置可以变化,那么怎么让所有线程都知道它的新位置在哪里呢?

详见stackoverflow: https://stackoverflow.com/questions/7557179/move-constructor-for-stdmutex

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程网的更多内容!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯