[英]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'. 它还可以使用
count
和value
参数并创建`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.