本文地址 https://www.cnblogs.com/wanger-sjtu/p/16876846.html

C++'s most vexing parse 是 Scott Meyers 在其名著《Effective STL》中创造的一个术语。Scott 用这个术语来形容 C++ 标准对于 declaration 语句的消歧义(ambiguity resolution)约定与常人的认知相悖。

最令人烦恼的解析 (most vexing parse)是C++中的一种反直觉的二义性解析形式。 在一些场景下,编译器无法区分某语句是初始化时某对象的参数,还是声明一个函数时指定参数类型。在这些情况下,编译器将该行解释为函数声明。

形如 Type() 或 Type(name) 的表达在某些情况下具有歧义(syntax ambiguity)。

C风格强制类型转换

void f(double my_dbl) {
int i(int(my_dbl));
}

上面的第 2 行是有歧义的。一种可能的解释是声明一个变量i,初始值通过转换my_dbl 到一个int而来。但是,C 允许在函数参数声明周围使用多余的括号;因此,声明的i实际上等同于以下代码:

// A function named i takes an integer and returns an integer.
int i(int my_dbl);

未命名的临时对象

struct Timer {};
struct TimeKeeper {
explicit TimeKeeper(Timer t);
int get_time();
}; int main() {
TimeKeeper time_keeper(Timer());
return time_keeper.get_time();
}

其中

TimeKeeper time_keeper(Timer());

是有歧义的,它可以被解释为:

  1. 一个变量:定义为类TimeKeeper的变量time_keeper,用类Timer的匿名实例初始化。
  2. 一个函数声明:声明了一个函数time_keeper,返回一个TimeKeeper,有一个(未命名的)参数。参数的类型是一个(指向)不接受输入并返回Timer对象的函数(的指针)。

[C ++标准]采取第二种解释,这与上面的第9行不一致。例如,Clang++警告第9行存在最令人烦恼的解析,并报错:

$ clang++ time_keeper.cc
**timekeeper.cc:9:25: warning: parentheses were disambiguated as a function declaration**
**[-Wvexing-parse]**
TimeKeeper time_keeper(Timer());
**^~~~~~~~~**
**timekeeper.cc:9:26: note:** add a pair of parentheses to declare a variable
TimeKeeper time_keeper(Timer());
^
( )
**timekeeper.cc:10:21: error: member reference base type 'TimeKeeper (Timer (*)())' is not a**
**structure or union**
return time_keeper.get_time();
**~~~~~~~~~~~^~~~~~~~~**

解决方案

这些有歧义的声明往往不会被解析为程序员所期望的语句。C++ 中的函数类型通常隐藏在typedef之后,并且通常具有显式引用或指针限定符。要强制扭转解析的结果,常见做法是换一种不同的对象创建或转换语法

在类型转换的示例中,有两种替代语法:“C 风格强制类型转换”

// declares a variable of type int
int i((int)my_dbl);

或一个static_cast转换:

int i(static_cast<int>(my_dbl));

在变量声明的示例中,首选方法(自 C++11 起)是统一(大括号)初始化。 这也允许完全省略类型名称:

//Any of the following work:
TimeKeeper time_keeper(Timer{});
TimeKeeper time_keeper{Timer()};
TimeKeeper time_keeper{Timer{}};
TimeKeeper time_keeper( {});
TimeKeeper time_keeper{ {}};

在 C++11 之前,强制获得预期解释的常用手段是使用额外的括号或拷贝初始化:

TimeKeeper time_keeper( /*Avoid MVP*/ (Timer())); // 增加一个括号
TimeKeeper time_keeper = TimeKeeper(Timer()); // c++ 17 拷贝运算可以被优化

C++'s most vexing parse的更多相关文章

  1. 【C++ 拾遗】C++'s most vexing parse

    C++'s most vexing parse 是 Scott Meyers 在其名著<Effective STL>中创造的一个术语. Scott 用这个术语来形容 C++ 标准对于 de ...

  2. Effective STL 笔记: Item 6--Be alert for C++'s most vexing parse

    假设有个文件里面记录的一系列的 int 值,现在我们想把这些数值存到一个 List 里面,结合 Item 5, 我们可能会写出下面的代码: ifstream dataFile("ints.d ...

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

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

  4. (转)C++语言的15个晦涩特性

    原文链接: Evan Wallace   翻译: 伯乐在线- 敏敏 译文链接: http://blog.jobbole.com/54140/ 这个列表收集了 C++ 语言的一些晦涩(Obscure)特 ...

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

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

  6. [译]GotW #1: Variable Initialization 续

    Answer 2. 下面每行代码都做了什么? 在Q2中,我们创建了一个vector<int>且传了参数10和20到构造函数中,第一种情况下(10,20),第二种情况是{10, 20}. 它 ...

  7. [译]GotW #1: Variable Initialization

    原文地址:http://herbsutter.com/2013/05/09/gotw-1-solution/ 第一个问题强调的是要明白自己在写什么的重要性.下面有几行简单的代码--它们大多数之间都有区 ...

  8. [Effective Modern C++] Item 7. Distinguish between () and {} when creating objects - 辨别使用()与{}创建对象的差别

    条款7 辨别使用()与{}创建对象的差别 基础知识 目前已知有如下的初始化方式: ); ; }; }; // the same as above 在以“=”初始化的过程中没有调用赋值运算,如下例所示: ...

  9. c++ 多线程 0

    1.1 何谓并发 最简单和最基本的并发,是指两个或更多独立的活动同时发生.  (注意区别于计算机中的并发情况!!!!!!!!!!见下面) 1.1.1 计算机系统中的并发:是指在单个系统里同时执行多个独 ...

  10. C++学习书籍推荐《Effective STL(英文)》下载

    百度云及其他网盘下载地址:点我 作者简介 Scott Meyers is one of the world's foremost authorities on C++, providing train ...

随机推荐

  1. 这可能是最全面的TCP面试八股文了

    计算机网络基础,考验一个程序员的基本功,也能更快的筛选出更优秀的人才. 说说TCP的三次握手 假设发送端为客户端,接收端为服务端.开始时客户端和服务端的状态都是CLOSED. 第一次握手:客户端向服务 ...

  2. 人工智能 deepface 换脸技术 学习

    介绍 Deepface是一个轻量级的python人脸识别和人脸属性分析(年龄.性别.情感和种族)框架.它是一种混合人脸识别框架缠绕状态的最先进的模型:VGG-Face,Google FaceNet,O ...

  3. day07 字符串和列表

    day07字符串与列表 字符串的内置方法 lower upper startswitch endwhich 格式化输出 format join的用法 replace替换字符串 isdigit判断 字符 ...

  4. SprintBoot2报错汇总

    报错1:SpringBoot找不到bean Unable to start ServletWebServerApplicationContext due to missing ServletWebSe ...

  5. Hyperledger Fabric 使用 CouchDB 和复杂智能合约开发

    前言 在上个实验中,我们已经实现了简单智能合约实现及客户端开发,但该实验中智能合约只有基础的增删改查功能,且其中的数据管理功能与传统 MySQL 比相差甚远.本文将在前面实验的基础上,将 Hyperl ...

  6. Cron表达式介绍与示例

      1. 概念介绍 Cron表达式是一个具有时间含义的字符串,字符串以5~6个空格隔开,分为6~7个域,格式为X X X X X X X.其中X是一个域的占位符.最后一个代表年份的域非必须,可省略.单 ...

  7. MINIO使用

    1.作用 官网地址:https://docs.min.io/ 文件存储.文件对象的上传.下载和删除! 2.使用依赖 <dependency> <groupId>io.minio ...

  8. 关于spring嵌套事务,我发现网上好多热门文章持续性地以讹传讹

    事情起因是,摸鱼的时候在某平台刷到一篇spring事务相关的博文,文章最后贴了一张图.里面关于嵌套事务的表述明显是错误的. 更奇怪的是,这张图有点印象.在必应搜索关键词PROPAGATION_NEST ...

  9. [[SCOI2008]城堡] 解题报告

    [SCOI2008]城堡 最大值最小,显然二分答案,但考虑二分后如何 check. \(n\) 个点 \(n\) 条边,显然这是一个基环树森林.对于基环树,常用的套路是拆环为链,枚举删去哪条边.但这题 ...

  10. 文章学习:TPRE:分布式门限代理重加密

    学习文章:TPRE:分布式门限代理重加密 前言 成方金科新技术实验室与隐语团队合作,构建了"基于国密的分布式门限代理重加密算法TPRE",为用户提供了一种安全.高效.自主可控的数据 ...