简体   繁体   English

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

[英]Is it best practice to use a initializer_list in assignment

I have read (Bjarne Stroustrup, The C++ Programming Language, 6.3.5) about using initializer_list when initializing a variable, so that you don't have a narrowing conversion. 我已经读过(Bjarne Stroustrup,The C ++ Programming Language,6.3.5)关于在初始化变量时使用initializer_list,这样你就没有缩小转换。 Bjarne recommends only using direct-list-initialization : Bjarne建议仅使用直接列表初始化:

X a1 {v}; X a1 {v};

X a2 = {v}; X a2 = {v};

X a3 = v; X a3 = v;

X a4(v); X a4(v);

Of these, only the first can be used in every context, and I strongly recommend its use. 其中,只有第一个可以在每个上下文中使用,我强烈建议使用它。 It is clearer and less error-prone than the alternatives. 它比其他选择更清晰,更不容易出错。

Why does Bjarne only recommend the first one? 为什么Bjarne只推荐第一个?

Why isn't it recommended to do initializer_list in assignment (rather than initialization)? 为什么不建议在赋值时执行initializer_list(而不是初始化)? Or is it just implied that you should do that? 或者只是暗示你应该这样做?

a1 = {v}; a1 = {v};

Here is an example of what I am asking about? 这是我要问的一个例子吗? Why is initializer_list not recommended for assignment (from what I can tell) but it is recommended for initialization? 为什么不建议使用initializer_list进行赋值(从我可以看出)但建议初始化? It seems like it is beneficial by reducing potential narrowing conversions on accident. 通过减少事故中可能缩小的转换似乎是有益的。

char c;
int toobig = 256;
c = 256; //no error, narrowing occurs
c = { toobig }; //narrowing conversion, error

Initializer lists are usually recommended and overcome a syntactic trap called 'The Most Vexxing Parse'. 通常建议使用初始化程序列表,并克服名为“The Most Vexxing Parse”的句法陷阱。

std::vector<int> v();  

Inside a function this looks like variable declaration but it's a declaration of a function v taking no arguments returning a std::vector<int> . 在函数内部,这看起来像变量声明,但它是函数v的声明,不带参数返回std::vector<int>

OK so std::vector<int> v; 好的,所以std::vector<int> v; fixes that one but in a template a parameter may be a built in type where int x; 修复了一个但在模板中的参数可能是内置类型,其中int x; leaves x uninitialized but int x{}; 叶子x未初始化但是int x{}; (value) initializes it to zero. (值)将其初始化为零。

In modern C++ with copy elision and a couple of syntatic rules, there aren't that many ways to accidentally create temporary copies in a variable declaration. 在具有copy elision和一些合成规则的现代C ++中,没有太多方法可以在变量声明中意外创建临时副本。

But initializer-lists do clear up a couple of anomalies and are to be recommended. 但初始化列表确实可以清除一些异常情况并被推荐。

Overloading in C++ is quite keen and there remain reasons to sometimes use () to call the appropriate constructor. C ++中的重载是非常敏锐的,有时候使用()来调用适当的构造函数仍然是有原因的。 For example std::vector<T> can take a initializer-list of values and create an array containing that list. 例如, std::vector<T>可以获取初始值列表的值并创建包含该列表的数组。 Great. 大。 It can also take count and value arguments and create an array of count copies of `value'. 它还可以使用countvalue参数并创建`value'的count副本数组。 Also great. 也很棒。

But if the size type is compatible with the value-type ( T ) you can still get a surprise! 但如果尺寸类型与值类型( 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";
}

Expected output: 预期产量:

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

The versions with () and {} did different things for std::vector<int> but landed on the same constructor for std::vector<std::string> . 带有(){}的版本对std::vector<int>了不同的操作,但是在std::vector<std::string>的相同构造函数上着陆。

This isn't a new or unique problem. 这不是一个新的或独特的问题。 C++ uses types heavily in selecting an overload and when there's a bunch of candidates different template instantiations might make an unexpected choice! C ++在选择重载时会大量使用类型,当有一堆候选者时,不同的模板实例化可能会出现意外的选择!

I'd say initializer-list is now preferred. 我说初始化列表现在是首选。 But when a constructor that itself takes in initializer list exists, you may need to be explict if you don't want to hit it. 但是当存在本身接收初始化列表的构造函数时,如果您不想点击它,则可能需要显示它。

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

Take this as example 以此为例

#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};

}

Compiling with g++ main.cpp --std=c++11 -fno-elide-constructors g++ main.cpp --std=c++11 -fno-elide-constructors编译g++ main.cpp --std=c++11 -fno-elide-constructors

Output: 输出:

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

creating object b
[+] c'tor called

In the first a temporary is created and then a is created by calling the copy constructor. 在第一个中创建temporary ,然后通过调用复制构造函数创建a

Whereas in second example the object is directly created. 而在第二个示例中,直接创建对象。

There are few cases where you have to use () syntax instead of {} . few cases ,您必须使用()语法而不是{}

Refer to Scott Meyers-Effective Modern C++ (item 7)` 请参阅Scott Meyers-Effective Modern C++ (第7项)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM