zoukankan      html  css  js  c++  java
  • [译]GotW #1: Variable Initialization 续

    Answer 2. 下面每行代码都做了什么?

    在Q2中,我们创建了一个vector<int>且传了参数10和20到构造函数中,第一种情况下(10,20),第二种情况是{10, 20}。

    它们都将调用构造函数,但是是哪个?vector<int>有好几个带两个参数的构造函数,但只有两个能在参数10和20下正确调用。为了简单,在此忽略了默认可选的allocator参数。这两个构造函数是:

    vector( size_t n, const int& value );    // A: n copies of value
    
    vector( initializer_list<int> values );  // B: copy of values

    有两个简单的C++规则可以告诉我们上面问题中哪个会被调用:

          · 在表达式上下文中使用语法{ /*…*/ }将会带来initializer_list。

          · 带initializer_list参数的构造函数会优先于其他构造函数,这样可以隐藏其他可能会是可行的构造函数。

    使用两个小缀饰,答案就变得简单了:

    vector<int> v1( 10, 20 );    // (a) calls A: 10 copies of the value 20
    assert( v1.size() == 10 );
    
    vector<int> v2{ 10, 20 };    // (b) calls B: the values 10 and 20
    assert( v2.size() == 2 );

    Answer 3. 除了上面代码,使用{}初始化对象还有什么其他好处?

    首先,它被称为“统一初始化”,因为它对所有类型都是一样的,包括aggregate structs、数组和std::containers,且没有恼人的“棘手的解析”:

    struct mystruct { int x, y; };
    
    // C++98 
    rectangle       w( origin(), extents() );       // oops, vexing parse 
    complex<double> c( 2.71828, 3.14159 ); 
    mystruct        m = { 1, 2 };
    int             a[] = { 1, 2, 3, 4 };
    vector<int>     v;                              // urk, need more code
    for( int i = 1; i <= 4; ++i ) v.push_back(i);   //   to initialize this
    
    // C++11 (note: "=" is mostly optional)
    rectangle       w   = { origin(), extents() }; 
    complex<double> c   = { 2.71828, 3.14159 }; 
    mystruct        m   = { 1, 2 }; 
    int             a[] = { 1, 2, 3, 4 };
    vector<int>     v   = { 1, 2, 3, 4 };

    要注意的是这不仅仅是一个美学上的问题。在写通用代码的时候应该能初始化任意类型,下面使用perfect forwarding演示一个例子:

    template<typename T, typename ...Args>
    void forwarder( Args&&... args ) {
        // ...
        T local = { std::forward<Args>(args)... };
        // ...
    }
    
    forwarder<int>            ( 42 );                  // ok
    forwarder<rectangle>      ( origin(), extents() ); // ok
    forwarder<complex<double>>( 2.71828, 3.14159 );    // ok
    forwarder<mystruct>       ( 1, 2 );                // ok because of {}
    forwarder<int[]>          ( 1, 2, 3, 4 );          // ok because of {}
    forwarder<vector<int>>    ( 1, 2, 3, 4 );          // ok because of {}

    最后3行如果在forwarder内部使用()来初始化的话是不合法的。

    新的{}语法在几乎在任何地方都能工作,包括初始化成员:

    widget::widget( /*...*/ ) : mem1{init1}, mem2{init2, init3} { /*...*/ }

    在传函数参数或者返回一个值(没有类型名的临时对象)也非常方便:

    void draw_rect( rectangle ); 
    
    draw_rect( rectangle(origin, selection) );         // C++98
    draw_rect({ origin, selection });                  // C++11
    
    rectangle compute_rect() {
       // ...
       if(cpp98) return rectangle(origin, selection);  // C++98
       else      return {origin, selection};           // C++11
    }

    Answer 4. 在什么时候应该使用()或者{ }语法来初始化对象?为什么?

    这有一些简单的指南:

          Guideline:优先使用{}进行初始化,比如:vector<int> v = { 1, 2, 3, 4 };或auto v = vector<int>{ 1, 2, 3, 4 };(译注:2)。因为它更一致,更正确,且完全避免了一些老式的陷阱。在单参数情况下,你可能会只看见=符号,比如:int i = 42;或者auto x = anything;省略花括号是OK的。。。

    这覆盖了大部分情况,只有一个主要的例外情况:

          在很少情况下,比如:vector<int> v(10,20);或者auto v = vector<int>(10,20);。显示地使用()被initializer_list构造函数隐藏的构造函数进行初始化。

    然而,这应该是很少见的情况,因为默认和拷贝构造函数已经可以和{}一起工作,一个类的好的设计为了用户定义的构造函数,现在应该通常避免还原到()的情况,所有有了最后一条设计指南:

          Guideline: 当设计一个类,避免提供一个与initializer_list构造函数有二义性的构造函数,因此用户不需要使用()来达到调用被隐藏的构造函数。

     

    ※:真心不太好翻译,这里red herring应该不是字面意思。

    ※2:这条语句至少在VS12 CTP版的编译器上是通不过编译的。http://rise4fun.com/Vcpp/r60

  • 相关阅读:
    ArcEngine9.3没有原生支持64位,而是以32位兼容方式运行
    记一次IIS应用程序域崩溃的原因
    切换添加[置顶] Behaviors扩展根据Pivot的item自动切换AppBar
    参数类型11g_job执行带参数的procedure
    元素返回[Python]python算法入门 栈(stack)
    模型案例复杂性思考
    执行目录glassfish不能远程登录问题
    文件目录IBM的LPI复习资料之LPI101Topic103 :GNU和Unix命令(3)文件和目录管理
    企业网站[正能量系列]失业的程序员(一)
    缓冲区方法你有被stringstream坑过吗?
  • 原文地址:https://www.cnblogs.com/navono007/p/3329835.html
Copyright © 2011-2022 走看看