class A{
public:
virtual ~A() {};
};
class B : public A{ };
int main(){
A&& p = B();
dynamic_cast<B&&>(std::move(p));
}
Run Code Online (Sandbox Code Playgroud)
抛出错误(g ++ 5.2.0):
error: conversion to non-const reference type 'std::remove_reference<A&>::type& {aka class A&}' from rvalue of type 'A' [-fpermissive]
Run Code Online (Sandbox Code Playgroud)
它试图转换std::move(p)
为类型A&
,但我无法弄清楚为什么.我会一直认为有必要投p
转换为右值引用之前作为右值,但如果我删除std::move
它编译罚款.从cppreference:
dynamic_cast < new_type > ( expression )
与其他强制转换表达式类似,结果如下:
如果new_type是左值引用类型(表达式必须是左值),则为左值
如果new_type是右值引用类型(表达式可能是左值或右值),则为xvalue
甚至是N3337的5.2.7:
dynamic_cast<T>(v)
如果T是指针类型,则v应该是指向完成类类型的指针的prvalue,结果是类型T的prvalue.如果T是左值引用类型,则v应该是完整类类型的左值,并且结果是由T 引用的类型的左值.如果T是右值引用类型,则v应该是具有完整类类型的表达式,并且结果是由T引用的类型的x值.
唯一要求我使用完整的类类型,std::move(p)
不是吗?
来自C++ Primer 18.1.1:
如果[thrown]表达式具有数组或函数类型,则表达式将转换为其对应的指针类型.
该程序如何产生9876543210
(g ++ 5.2.0)的正确输出?
#include <iostream>
using namespace std;
int main(){
try{
int a[10] = {9,8,7,6,5,4,3,2,1,0};
throw a;
}
catch(int* b) { for(int i = 0; i < 10; ++i) cout << *(b+i); }
}
Run Code Online (Sandbox Code Playgroud)
从引用中,throw a
将创建一个类型的异常对象,int*
它是指向数组的第一个元素的指针.但是a
当我们退出try
块并输入catch子句时,我们肯定会破坏数组元素,因为我们改变了块范围?在catch子句的持续时间内,我是否得到了误报或数组元素"单独留下"(未删除)?
如果没有(因为标题中定义了这个错误),new
程序中的表达式如何抛出错误是怎么回事?bad_alloc
#include <new>
<new>
从3.7.4开始.N3337:
该库提供全局分配和释放功能的默认定义.一些全局分配和释放功能是可替换的(18.6.1).C++程序最多只能提供一个可替换分配或释放功能的定义.任何此类函数定义都将替换库中提供的默认版本(17.6.4.6).以下分配和释放函数(18.6)在程序的每个转换单元中在全局范围中隐式声明.
Run Code Online (Sandbox Code Playgroud)void* operator new(std::size_t); void* operator new[](std::size_t); void operator delete(void*); void operator delete[](void*);
这些隐含的声明只介绍了函数名
operator new
,operator new[]
,operator delete
,和operator delete[]
.[ 注:隐式声明不引入名称std
,std::size_t
或该库使用来声明这些名称的任何其他名称.因此,在不包括头部的情况下引用这些函数之一的新表达式,删除表达式或函数调用<new>
是格式良好的.但是,除非通过包含适当的标题声明了名称,否则引用std
或std::size_t
形成不良.-end note ]也可以为任何类声明和定义分配和/或释放函数
这仍然不清楚.隐式声明使用std::size_t
但不引入它们(同样必须是这样bad_alloc
)?并且std::size_t
在new
表达式可以使用之前不需要引入?可以理解这是怎么回事,或者我必须从表面上看它?
我正在阅读Scott Meyers撰写的Effective Modern C++,他正在讨论使用pimpl习语并指向实现类unique_ptr
,但是存在需要完整类型的特殊成员函数(例如析构函数)的问题.这是因为unique_ptr
在delete p
使用之前,默认删除器静态断言要删除的类型是否完整.所以任何类特殊的成员函数必须在实现文件中定义(而不是编译器生成的),之后实现类已定义.
在本章的最后,他提到如果使用的是智能指针,则不需要在实现文件中定义特殊的成员函数shared_ptr
,这源于它支持自定义删除器的方式.报价:
pImpl指针的std :: unique_ptr和std :: shared_ptr之间的行为差异源于这些智能指针支持自定义删除器的不同方式.对于std :: unique_ptr,删除器的类型是智能指针类型的一部分,这使编译器可以生成更小的运行时数据结构和更快的运行时代码.这种更高效率的结果是,当使用编译器生成的特殊函数(例如,析构函数或移动操作)时,指向类型必须是完整的.对于std :: shared_ptr,删除器的类型不是智能指针类型的一部分.这需要更大的运行时数据结构和稍慢的代码,但是当使用编译器生成的特殊函数时,指向的类型不需要完整.
尽管如此,我仍然不明白为什么shared_ptr
没有完成课程仍然可以工作.这似乎是使用时没有编译器错误的唯一原因shared_ptr
是因为没有像has那样的静态断言unique_ptr
,并且由于缺少断言而可能会发生未定义的运行时行为.
我不知道shared_ptr
析构函数的实现,但是(从阅读C++ Primer)我收集的印象它的工作原理如下:
del ? del(p) : delete p;
Run Code Online (Sandbox Code Playgroud)
del
自定义删除器的指针或函数对象在哪里.Cppreference还清楚地说明shared_ptr
了没有自定义删除器使用的析构函数delete p
3)
delete ptr
如果T
不是数组类型,则使用delete-expression ; .... Y必须是完整的类型.删除表达式必须格式正确,具有明确定义的行为并且不会抛出任何异常.
强调删除类型必须完整的事实.pimpl习语的最小例子:
//widget.h
#ifndef WIDGET
#define WIDGET
#include <memory>
class Widget{
public:
Widget();
private:
struct Impl;
std::shared_ptr<Impl> pImpl;
};
#endif // …
Run Code Online (Sandbox Code Playgroud) 为什么使用hexfloat
操纵器的输出会忽略任何精度ostream
?
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
int main(){
cout << setw(17) << left << "default format: " << setw(20) << right << 100 * sqrt(2.0) << " " << cout.precision() << '\n'
<< setw(17) << left << "scientific: " << setw(20) << right << scientific << 100 * sqrt(2.0) << " " << cout.precision() << '\n'
<< setw(17) << left << "fixed decimal: " << fixed << setw(20) << right << 100 …
Run Code Online (Sandbox Code Playgroud) c++ ×5
c++11 ×5
arrays ×1
bad-alloc ×1
dynamic-cast ×1
hex ×1
pimpl-idiom ×1
precision ×1
rvalue ×1
throw ×1