本周小贴士#88:初始化:=,()和{}

作为TotW#88最初发表于2015年6月27日

由Titus Winters (titus@google.com)创作,代表Google C++风格仲裁者

C++11提供了一种称为“统一初始化语法”的新语法,它被认为统一所有不同风格的初始化,避免最烦人的解析,并避免窄化转换。这种新机制意味着我们现在有另一种初始化语法,它有自己的权衡。

C++11括号初始化

一些统一初始化语法的支持者会建议我们使用{}和直接初始化(不使用‘=’,尽管在多数情况下两种形式调用相同的构造函数)来初始化所有类型:

int x{2};
std::string foo{"Hello World"};
std::vector<int> v{1, 2, 3};

对比(例如):

int x = 2;
std::string foo = "Hello World";
std::vector<int> v = {1, 2, 3};

这种方法有两个特点。首先,“统一”是一个延伸概念:在某些情况下,在调用什么以及如何调用这方面依然存在歧义(针于普通读者,而不是编译器)。

std::vector<std::string> strings{2}; // 两个空字符串的vector
std::vector<int> ints{2};            // 仅仅一个整数2的vector

其次:这种语法并不完全直观:没有其他通用语法像这样使用。这种语言当然可以引入新的令人惊讶的语法,并且在某些情况下有必要使用它的技术原因——尤其是在通用代码中。重要的问题是:我们应该在多大程度上改变我们的习惯和语言理解来利用这种改变?改变我们的习惯或存在的代码,这种代价是否值得?对于统一初始化语法,我们一般不认为利大于弊。

关于初始化的最佳实践

相反,我们推荐下面的指南来进行”如何初始化一个变量?”,既可以在你自己的代码中遵循,也可以你的代码审查中引用:

  • 当用预期的字面量(例如:int,float或std::string值)、智能指针(例如std::shared_ptr,std::unqie_ptr)、容器(std::vector,std::map等)来直接初始时,当执行结构体初始化或进行深拷贝时,请使用赋值语法。
int x = 2;
std::string foo = "Hello World";
std::vector<int> v = {1, 2, 3};
std::unique_ptr<Matrix> matrix = NewMatrix(rows, cols);
MyStruct x = {true, 5.0};
MyProto copied_proto = original_proto;

而不是:

// 糟糕的代码
int x{2};
std::string foo{"Hello World"};
std::vector<int> v{1, 2, 3};
std::unique_ptr<Matrix> matrix{NewMatrix(rows, cols)};
MyStruct x{true, 5.0};
MyProto copied_proto{original_proto};
  • 当初始化正在执行一些活动的逻辑时,使用传统的构造函数语法(用括号),而不是简单地将值组合在一起。
Frobber frobber(size, &bazzer_to_duplicate);
std::vector<double> fifty_pies(50, 3.14);

对比

// 糟糕的代码
// 可以调用一个初始化列表构造函数,或者一个两个参数的构造函数 
Frobber frobber{size, &bazzer_to_duplicate};

// 制作一个2个double的vector
std::vector<double> fifty_pies{50, 3.14};
  • 只有以上选项不能编译时,才使用{}初始化而不用=
class Foo {
 public:
  Foo(int a, int b, int c) : array_{a, b, c} {}

 private:
  int array_[5];
  // 因为构造函数标记为explicit并且是非拷贝类型,因此需要{}
  EventManager em{EventManager::Options()};
};
  • 绝不滥用{}和auto
    例如,不要这样做:
// 糟糕的代码
auto x{1};
auto y = {2}; // 这是std::initializer_list<int>!

(对于语言律师而言:如果可行,优先使用拷贝初始化而不是直接初始化,并在直接初始化时使用圆括号而不是大括号。)

也许对这个问题最完整的描述是Herb Sutter的GotW贴文。尽管他展示了一个直接用大括号初始化int的示例,但是他最终的建议大致与我们在此提出的建议相符:Herb说“在那里你更愿意只看到=符号”,我们毫不含糊地优先确定地看到那个。结合更加一致地在多参数构造函数上使用explicit(参见贴士#142),这提供了在可读性、确切性和正确性之间的平衡。

结论

统一初始化语法的权衡结果通常是不值得的:我们的编译器已经警告了最烦人的解析(你可以使用大括号初始化或添加括号来解决这个问题),并且窄化转换的安全性不值得为可读性去使用大括号初始化(我们最终需要一个不同的解决方案来进行窄化转换)。风格仲裁者认为这个问题不足以制作一个正式的规则,特别是因为在某些情况下(尤其是在通用代码中)大括号初始化可能是合理的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值