本文地址 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. 每日复习——static , 饿汉式方法,懒汉式方法,以及单例设计模式

    1.1.static 的使用 当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过 new 关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部 ...

  2. Python中实现单例的几种方式

    Python如何实现单例? 什么是单例模式? 单例模式:一个类只能有一个实例化对象存在的模式. 如何实现单例? 1.使用模块 python中模块是天然的单例模式,当一个模块被调用时,会生成对应的.py ...

  3. python线程之event事件

    from threading import Thread, Event import time event = Event() def light(): print('红灯亮着,所有车都要等待') t ...

  4. ThreadLocal、进程VS线程、分布式进程

    1.ThreadLocal变量是一个全局变量,每个线程只能读取自己的独立副本,ThreadLocal解决了一个线程中各个函数之间的传递问题 import threading local_school ...

  5. Vulnhub Development靶场 Walkthrough

    Recon 首先使用netdiscover进行二层Arp扫描. ┌──(kali㉿kali)-[~] └─$ sudo netdiscover -r 192.168.80.0/24 Currently ...

  6. python选出一定数量的随机文件到某个文件夹

    import os import random import shutil def move_file(target_path, save_path, number): file_list = os. ...

  7. 手动编写Swagger文档与部署指南

    Swagger介绍 在Web开发中,后端开发者在完成接口开发后,需要给前端相应的接口使用说明,所以一般会写一份API文档.一般来说,有两种方式提供API接口文档,一种是利用插件在代码中自动生成,另一种 ...

  8. 【vue3-element-admin 】基于 Vue3 + Vite4 + TypeScript + Element-Plus 从0到1搭建后台管理系统(前后端开源@有来开源组织)

    vue3-element-admin 是基于 vue-element-admin 升级的 Vue3 + Element Plus 版本的后台管理前端解决方案,技术栈为 Vue3 + Vite4 + T ...

  9. Vue的项目打包为移动端(安卓手机应用)app

    现在基于vue全家桶技术体系,基本上可以开发各端的各种应用,pc端的应用,开发完成以后,直接运行打包命令 yarn build 即可打包,部署到服务器端上线即可.那么,今天我们来聊一聊,开发好的vue ...

  10. Godot 4.0 文件读取(C#)

    搞半天才弄明白Godot文件操作. Godot的文档总是试图让我使用自定义Resource来支持文件操作,但是我只需要读取纯文本. 读取纯文本 读取纯文本的方式如下: //Godot.FileAcce ...