最佳做法是在赋值中使用initializer_list

时间:2018-06-15 07:41:42

标签: c++ c++11

我已经读过(Bjarne Stroustrup,C ++编程语言,6.3.5)关于在初始化变量时使用initializer_list,这样你就没有缩小转换。 Bjarne建议仅使用直接列表初始化:

  

X a1 {v};

     

X a2 = {v};

     

X a3 = v;

     

X a4(v);

     

其中,只有第一个可以在每个环境中使用,我强烈地   推荐使用它。它更清晰,更不容易出错   的替代品。

为什么Bjarne只推荐第一个?

为什么不建议在赋值中执行initializer_list(而不是初始化)?或者只是暗示你应该这样做?

  

a1 = {v};

这是我要问的一个例子吗?为什么不建议使用initializer_list进行赋值(从我可以看出)但建议初始化?通过减少事故中可能缩小的转换率似乎是有益的。

System.Runtime.CompilerServices.Unsafe

2 个答案:

答案 0 :(得分:1)

通常建议使用初始化列表,并克服一个名为“最Vexxing Parse”的语法陷阱。

std::vector<int> v();  

在函数内部,这看起来像变量声明,但它是一个函数v的声明,不会返回std::vector<int>的参数。

好的,std::vector<int> v;修复了一个但在模板中的参数可能是内置类型,其中int x;使x未初始化但int x{};(值)将其初始化为零。

在具有copy elision和一些合成规则的现代C ++中,没有多种方法可以在变量声明中意外创建临时副本。

但初始化列表确实可以清除一些异常情况并被推荐。

C ++中的重载非常敏锐,有时仍然有理由使用()来调用相应的构造函数。 例如,std::vector<T>可以获取初始值列表的值并创建包含该列表的数组。大。 它还可以使用countvalue个参数,并创建一个{value}的count副本数组。也很棒。

但是如果尺寸类型与值类型(T)兼容,您仍会感到意外!

#include <iostream>
#include <vector>

template<typename T>
void dump_vector(const std::string& tag,const std::vector<T>& vec);    

int main() {

    std::vector<int> v1(5,20);
    std::vector<int> v2{5,20};
    std::vector<std::string> v3(5,"Hi!");
    std::vector<std::string> v4{5,"Hi!"};

    dump_vector("v1",v1);
    dump_vector("v2",v2);
    dump_vector("v3",v3);
    dump_vector("v4",v4);

    return 0;
}

template<typename T>
void dump_vector(const std::string& tag,const std::vector<T>& vec){
    std::cout<< tag << "={ ";
    auto begin=vec.begin();
    auto end=vec.end();
    for(auto it{begin};it!=end;++it){
        if(it!=begin){
            std::cout<<", ";
        }
        std::cout<<*it;
    }
    std::cout << " }\n";
}

预期产出:

v1={ 20, 20, 20, 20, 20 }
v2={ 5, 20 }
v3={ Hi!, Hi!, Hi!, Hi!, Hi! }
v4={ Hi!, Hi!, Hi!, Hi!, Hi! }

(){}的版本为std::vector<int>做了不同的事情,但落在std::vector<std::string>的相同构造函数上。

这不是一个新的或独特的问题。 C ++在选择重载时会大量使用类型,当有一堆候选者时,不同的模板实例可能会出现意外的选择!

我说初始化列表现在是首选。但是当一个构造函数本身存在初始化列表时,如果你不想点击它,你可能需要被删除。

另外值得一读http://read:%20https://herbsutter.com/2013/05/09/gotw-1-solution/

答案 1 :(得分:-1)

以此为例

#include <iostream>

struct foo
{
    explicit foo(int)
    {
        std::cout << "[+] c'tor called\n";
    }

    foo(const foo&)
    {
        std::cout << "[+] copy c'tor called\n";
    }

};

int main()
{

    std::cout << "\ncreating object a\n";
    foo a = foo{1};

    std::cout << "\n\ncreating object b\n";
    foo b{1};

}

使用g++ main.cpp --std=c++11 -fno-elide-constructors

进行编译

输出:

creating object a
[+] c'tor called
[+] copy c'tor called

creating object b
[+] c'tor called

在第一个创建temporary,然后通过调用复制构造函数创建a

而在第二个例子中,直接创建了对象。

few cases您必须使用()语法而不是{}Scott Meyers-Effective Modern C++

参考svn merge BRANCH TRUNK(第7项)`