Effective Modern C++ 学习笔记3——()和{

2023-04-01  本文已影响0人  拔丝圣代

几个例子探讨区别

C++中初始化方式有很多种,下面用几个例子来对比其区别。

  1. 以下3种初始化方式合法吗?
int x = 1.0;
int x(1.0);
int z{1.0};

答案:x和y的初始化合法,z的初始化不合法。

原因:大括号初始化内建类型时,禁止隐式窄化类型转换。

所谓窄化类型转换,指的是类型转换可能丢失精度,比如double转为int;反之则允许。

  1. 下面几行声明的是对象还是函数?
// 假设已有class Widget的定义
Widget w1;
Widget w2();
Widget w3(1);
Widget w4{};
Widget w5{1};

答案:w2声明了一个函数,返回值类型为Widget,其他几种都声明了一个Widget对象。

这就是most vexing parse。对比w2与w3,w3传入一个int类型参数,调用的是Widget(int)这个构造函数;如果想要调用Widget的默认构造函数,而去掉括号中的参数,却声明了一个返回Widget的函数。

而将小括号换成大括号,w4和w5则可以避免这个问题,声明的一定是对象而非函数。

  1. 下面的代码分别声明了什么样的vector?
std::vector<int> v1(10,20);
std::vector<int> v2{10,20};

答案:v1长度为10,所有元素为20;v2长度为2,元素分别为10, 20。

为什么这里用小括号和大括号有如此大的区别?可以简单的理解为vector包含这样两个构造函数:

vector(int, int);
vector(std::initializer_list);

而当使用大括号初始化时,总是会优先匹配std::initializer_list类型的参数

模板中如何选择?

在平时的代码中,我们一般可以选择其中一种作为默认,另一种只在必要时使用。

而在模板的编写中,这是一个非常头疼的问题。

例如,想要创建一个模板,能够以任意数量参数创建任意类型对象,可以这样写:

template<typename T, typename... Ts>
void doSomeWork(TS&&.. params) {
  T localObject(std::forward<Ts>(params)...); // 选择采用小括号
  T localObject{std::forward<Ts>(params)...); // 或选择采用大括号
}

那么,结合上面的例子,当我们这样使用时:

doSomeWork<std::vector<int>>(10, 20);

到底是创建了长度为10,每个值为20的vector对象,还是创建了长度为2,值分别为10、20的vector对象呢?

这便取决于doSomeWork内部实现中,到底使用的是小括号还是大括号。

这正是std::make_unique和std::make_shared所面临的问题,目前的实现是,内部使用的小括号。

上一篇下一篇

猜你喜欢

热点阅读