C++'s most vexing parse

直接看一个例子:

#include<iostream>
#include<thread>
#include <chrono>using namespace std::chrono_literals;class task {public:void operator() () {std::cout << "task begin..." << std::endl;std::this_thread::sleep_for(2s);std::cout << "task done!" << std::endl;}
};int main() {std::thread t(task());t.join();return 0;
}

这段代码很简单,在主线程(main函数)中起动一个子线程,并等待子线程执行完成。然而,上面代码编译会报错:

test.cpp: In function ‘int main()’:
test.cpp:18:7: error: request for member ‘join’ in ‘t’, which is of non-class type ‘std::thread(task (*)())’18 |     t.join();|       ^~~~

因为 t 根本就不是一个类类型的对象,而是一个函数,这里的 std::thread t(task()) 并非创建一个 std::thread 对象并启动该线程执行,而是申明了一个函数,该函数的返回值类型为 std::thread ,函数名为 t,参数为一个函数指针(返回值类型为 task,无函数名,参数为空)。这就是本文的主题:C++'s most vexing parse(C++最令人费解的解析)。Scott Meyers 在《Effective STL》中的 Item 6 有相关介绍。

在 C++ 中,加入我们要申明一个函数 f ,返回值类型为 int,参数类型为 double,则以下 3 种都是合法的申明方式:

int f(double d);
int f(double (d));
int f(double);

第二个申明,对行参加上圆括号,编译器会认为是冗余的并忽略。第三个省略了行参名。

如果我们想申明一个函数,并且其参数是一个函数指针,下面 3 种写法都是合法的:

int g(double (*pf)());
int g(double pf());
int g(double ());

第二个申明中行参省去指针类型也是合法的,第三个申明中行参直接省去了函数名也是合法的。

更普遍地,对于下面的语句:

T1 name(T2());
T1 name1(T2(name2));

C++都会将其视为函数申明:

  • 对于第一个语句,C++将其视为:返回值类型为 T1 名为 name 的函数(参数类型为指向返回值类型为 T2,参数为空的函数的指针)。
  • 对于第二个语句,C++将其视为:T1 name1(T2 name2); 显然也是一个函数申明。

再看本文开头给出的例子,就不难理解编译报错的原因了。解决办法也比较多,比如:

std::thread t((task()));std::thread t{task()};task f
std::thread t(f);

至此,本文结束,希望对你有所帮助。

参考:

  • Scott Meyers 《Effective STL》
  • http://eel.is/c++draft/dcl.ambig.res
  • https://www.fluentcpp.com/2018/01/30/most-vexing-parse/

C++‘s most vexing parse相关推荐

  1. C++ Most vexing parse(C++最头疼的解析)

    首先需要了解,在C++中,如下三种方式声明了同一个函数: int f(double d): //声明接受一个double参数d,返回值为int类型的函数 int f(double (d))://效果一 ...

  2. 将整个ASCII文件读入C ++ std :: string [重复]

    本文翻译自:Read whole ASCII file into C++ std::string [duplicate] This question already has an answer her ...

  3. [转]C++ 11 多线程--线程管理

    转载地址:https://www.cnblogs.com/wangguchangqing/p/6134635.html 说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段.并 ...

  4. C/C++ 中生成特定范围内的随机数

    大家在写 C/C++ 程序时,难免会遇到要求获取某个范围内的随机数,我查阅了一些资料后,总结如下.本文分两部分,先介绍 C 语言中与随机数相关的两个函数 srand 和 rand,后介绍 C++ 中的 ...

  5. C++ 11 多线程--线程管理

    说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段.并行是指两个或多个独立的操作同时进行.注意这里是同时进行,区别于并发,在一个时间段内执行多个操作.在单核时代,多个线程是并 ...

  6. C++你不知道的那些事儿—C++语言的15个晦涩特性

    这个列表收集了 C++ 语言的一些晦涩(Obscure)特性,是我经年累月研究这门语言的各个方面收集起来的.C++非常庞大,我总是能学到一些新知识.即使你对C++已了如指掌,也希望你能从列表中学到一些 ...

  7. 面经——C/C++常见面试知识点总结附面试真题

    参考:C/C++ 面试题 作者:zhaouc 发布时间: 2015-02-15 15:51:00 网址:https://blog.csdn.net/zhaouc/article/details/438 ...

  8. C++11多线程----线程管理

    说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段.并行是指两个或多个独立的操作同时进行.注意这里是同时进行,区别于并发,在一个时间段内执行多个操作.在单核时代,多个线程是并 ...

  9. 大括号之谜:C++的列表初始化语法解析

    转载: https://segmentfault.com/a/1190000039362151 摘要:有朋友在使用std::array时发现一个奇怪的问题:当元素类型是复合类型时,编译通不过. 有朋友 ...

最新文章

  1. CocosCreator TOUCH_MOVE事件
  2. Java虚拟机2:Java 运行时数据区
  3. rtsp协议_如何在RTSP协议视频智能平台EasyNVR未登录的情况下调用通道直播的接口?...
  4. Play on Words UVA - 10129 (有向图欧拉路径)
  5. USB device如何进入suspend模式
  6. 再谈迭代器,生成器,yield,及和类的使用
  7. 流媒体技术的应用与发展前景
  8. UI设计素材|app表单模板,临摹学习,有效提高设计水平!
  9. 默认栅格大小为多少_用于创建空栅格的ST_MakeEmptyRaster函数
  10. Liststring绑定到DataGridView控件
  11. JTT808、JTT809、JTT796、JTT794、JTT1077、JTT1078区别与交通部道路运输车辆卫星定位系统部标标准大全下载地址...
  12. 正则表达式网站在线测试
  13. 讲解c程序设计语言的比喻,《C语言程序设计》论文关于比喻在《C语言程序设计》课程教学中的应用论文范文参考资料...
  14. 纽约峭石之巅观景台:从直入云天的城市之巅眺望纽约全景
  15. mybatis lazyload
  16. SAP MM 物料主数据-物料版次-基础应用
  17. ES20-JAVA API 词项搜索
  18. stm32--数码管
  19. 还记得,你发送的第一封电子邮件是什么时候吗?
  20. 博弈论-斐波那契博弈

热门文章

  1. Pr剪切的视频在优酷上播放为绿屏的解决方案
  2. input在iphone下显示上边框解决方案
  3. 基于微信小程序带后端ssm接口小区物业管理平台设计
  4. sed与awk笔记分享
  5. buaacoding ?.海洋与陆地
  6. JAVA+MYSQL+CSV用正则表达式获取CPU天梯分数
  7. MFC 实现检测ppt是否正在播放
  8. 在react中使用fetch
  9. 基于javaweb+jsp的茶叶售卖商城系统(java+SSM+JSP+EasyUi+mysql)
  10. (免费分享)基于springboot健康运动-带论文