C++'s most vexing parse
本文地址 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());
是有歧义的,它可以被解释为:
- 一个变量:定义为类
TimeKeeper的变量time_keeper,用类Timer的匿名实例初始化。 - 一个函数声明:声明了一个函数
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的更多相关文章
- 【C++ 拾遗】C++'s most vexing parse
C++'s most vexing parse 是 Scott Meyers 在其名著<Effective STL>中创造的一个术语. Scott 用这个术语来形容 C++ 标准对于 de ...
- Effective STL 笔记: Item 6--Be alert for C++'s most vexing parse
假设有个文件里面记录的一系列的 int 值,现在我们想把这些数值存到一个 List 里面,结合 Item 5, 我们可能会写出下面的代码: ifstream dataFile("ints.d ...
- C++ 11 多线程--线程管理
说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段.并行是指两个或多个独立的操作同时进行.注意这里是同时进行,区别于并发,在一个时间段内执行多个操作.在单核时代,多个线程是并 ...
- (转)C++语言的15个晦涩特性
原文链接: Evan Wallace 翻译: 伯乐在线- 敏敏 译文链接: http://blog.jobbole.com/54140/ 这个列表收集了 C++ 语言的一些晦涩(Obscure)特 ...
- C++你不知道的那些事儿—C++语言的15个晦涩特性
这个列表收集了 C++ 语言的一些晦涩(Obscure)特性,是我经年累月研究这门语言的各个方面收集起来的.C++非常庞大,我总是能学到一些新知识.即使你对C++已了如指掌,也希望你能从列表中学到一些 ...
- [译]GotW #1: Variable Initialization 续
Answer 2. 下面每行代码都做了什么? 在Q2中,我们创建了一个vector<int>且传了参数10和20到构造函数中,第一种情况下(10,20),第二种情况是{10, 20}. 它 ...
- [译]GotW #1: Variable Initialization
原文地址:http://herbsutter.com/2013/05/09/gotw-1-solution/ 第一个问题强调的是要明白自己在写什么的重要性.下面有几行简单的代码--它们大多数之间都有区 ...
- [Effective Modern C++] Item 7. Distinguish between () and {} when creating objects - 辨别使用()与{}创建对象的差别
条款7 辨别使用()与{}创建对象的差别 基础知识 目前已知有如下的初始化方式: ); ; }; }; // the same as above 在以“=”初始化的过程中没有调用赋值运算,如下例所示: ...
- c++ 多线程 0
1.1 何谓并发 最简单和最基本的并发,是指两个或更多独立的活动同时发生. (注意区别于计算机中的并发情况!!!!!!!!!!见下面) 1.1.1 计算机系统中的并发:是指在单个系统里同时执行多个独 ...
- C++学习书籍推荐《Effective STL(英文)》下载
百度云及其他网盘下载地址:点我 作者简介 Scott Meyers is one of the world's foremost authorities on C++, providing train ...
随机推荐
- 每日复习——static , 饿汉式方法,懒汉式方法,以及单例设计模式
1.1.static 的使用 当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过 new 关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部 ...
- Python中实现单例的几种方式
Python如何实现单例? 什么是单例模式? 单例模式:一个类只能有一个实例化对象存在的模式. 如何实现单例? 1.使用模块 python中模块是天然的单例模式,当一个模块被调用时,会生成对应的.py ...
- python线程之event事件
from threading import Thread, Event import time event = Event() def light(): print('红灯亮着,所有车都要等待') t ...
- ThreadLocal、进程VS线程、分布式进程
1.ThreadLocal变量是一个全局变量,每个线程只能读取自己的独立副本,ThreadLocal解决了一个线程中各个函数之间的传递问题 import threading local_school ...
- Vulnhub Development靶场 Walkthrough
Recon 首先使用netdiscover进行二层Arp扫描. ┌──(kali㉿kali)-[~] └─$ sudo netdiscover -r 192.168.80.0/24 Currently ...
- python选出一定数量的随机文件到某个文件夹
import os import random import shutil def move_file(target_path, save_path, number): file_list = os. ...
- 手动编写Swagger文档与部署指南
Swagger介绍 在Web开发中,后端开发者在完成接口开发后,需要给前端相应的接口使用说明,所以一般会写一份API文档.一般来说,有两种方式提供API接口文档,一种是利用插件在代码中自动生成,另一种 ...
- 【vue3-element-admin 】基于 Vue3 + Vite4 + TypeScript + Element-Plus 从0到1搭建后台管理系统(前后端开源@有来开源组织)
vue3-element-admin 是基于 vue-element-admin 升级的 Vue3 + Element Plus 版本的后台管理前端解决方案,技术栈为 Vue3 + Vite4 + T ...
- Vue的项目打包为移动端(安卓手机应用)app
现在基于vue全家桶技术体系,基本上可以开发各端的各种应用,pc端的应用,开发完成以后,直接运行打包命令 yarn build 即可打包,部署到服务器端上线即可.那么,今天我们来聊一聊,开发好的vue ...
- Godot 4.0 文件读取(C#)
搞半天才弄明白Godot文件操作. Godot的文档总是试图让我使用自定义Resource来支持文件操作,但是我只需要读取纯文本. 读取纯文本 读取纯文本的方式如下: //Godot.FileAcce ...