B. Stroustrup - The C++ Programming Language (794319), страница 41
Текст из файла (страница 41)
For example, float to double is allowed, but not double to float.• A floating-point value cannot be converted to an integer type.• An integer value cannot be converted to a floating-point type.For example:void f(double val, int val2){int x2 = val;// if val==7.9, x2 becomes 7char c2 = val2;// if val2==1025, c2 becomes 1160Types and DeclarationsChapter 6int x3 {val};char c3 {val2};// error : possible truncation// error : possible narrowingchar c4 {24};char c5 {264};// OK: 24 can be represented exactly as a char// error (assuming 8-bit chars): 264 cannot be represented as a charint x4 {2.0};// error: no double to int value conversion// ...}See §10.5 for the conversion rules for built-in types.There is no advantage to using {} initialization, and one trap, when using auto to get the typedetermined by the initializer.
The trap is that if the initializer is a {}-list, we may not want its typededuced (§6.3.6.2). For example:auto z1 {99};auto z2 = 99;// z1 is an initializer_list<int>// z2 is an intSo prefer = when using auto.It is possible to define a class so that an object can be initialized by a list of values and alternatively be constructed given a couple of arguments that are not simply values to be stored.
The classical example is a vector of integers:vector<int> v1 {99};vector<int> v2(99);// v1 is a vector of 1 element with the value 99// v2 is a vector of 99 elements each with the default value 0I use the explicit invocation of a constructor, (99), to get the second meaning. Most types do notoffer such confusing alternatives – even most vectors do not; for example:vector<string> v1{"hello!"};vector<string> v2("hello!");// v1 is a vector of 1 element with the value "hello!"// error : no vector constructor takes a string literalSo, prefer {} initialization over alternatives unless you have a strong reason not to.The empty initializer list, {}, is used to indicate that a default value is desired.
For example:int x4 {};double d4 {};char∗ p {};vector<int> v4{};string s4 {};// x4 becomes 0// d4 becomes 0.0// p becomes nullptr// v4 becomes the empty vector// s4 becomes ""Most types have a default value. For integral types, the default value is a suitable representation ofzero. For pointers, the default value is nullptr (§7.2.2).
For user-defined types, the default value (ifany) is determined by the type’s constructors (§17.3.3).For user-defined types, there can be a distinction between direct initialization (where implicitconversions are allowed) and copy initialization (where they are not); see §16.2.6.Initialization of particular kinds of objects is discussed where appropriate:• Pointers: §7.2.2, §7.3.2, §7.4• References: §7.7.1 (lvalues), §7.7.2 (rvalues)Section 6.3.5••••Initialization161Arrays: §7.3.1, §7.3.2Constants: §10.4Classes: §17.3.1 (not using constructors), §17.3.2 (using constructors), §17.3.3 (default),§17.4 (member and base), §17.5 (copy and move)User-defined containers: §17.3.46.3.5.1 Missing InitializersFor many types, including all built-in types, it is possible to leave out the initializer.
If you do that– and that has unfortunately been common – the situation is more complicated. If you don’t likethe complications, just initialize consistently. The only really good case for an uninitialized variable is a large input buffer. For example:constexpr int max = 1024∗1024;char buf[max];some_stream.get(buf,max); // read at most max characters into bufWe could easily have initialized buf:char buf[max] {};// initialize every char to 0By redundantly initializing, we would have suffered a performance hit which just might have beensignificant. Avoid such low-level use of buffers where you can, and don’t leave such buffers uninitialized unless you know (e.g., from measurement) that the optimization compared to using an initialized array is significant.If no initializer is specified, a global (§6.3.4), namespace (§14.3.1), local static (§12.1.8), orstatic member (§16.2.12) (collectively called static objects) is initialized to {} of the appropriatetype.
For example:int a;double d;// means ‘‘int a{};’’ so that a becomes 0// means ‘‘double d{};’’ so that d becomes 0.0Local variables and objects created on the free store (sometimes called dynamic objects or heapobjects; §11.2) are not initialized by default unless they are of user-defined types with a defaultconstructor (§17.3.3). For example:void f(){int x;char buf[1024];}// x does not have a well-defined value// buf[i] does not have a well-defined valueint∗ p {new int};char∗ q {new char[1024]};// *p does not have a well-defined value// q[i] does not have a well-defined valuestring s;vector<char> v;// s=="" because of string’s default constructor// v=={} because of vector’s default constructorstring∗ ps {new string};// ...// *ps is "" because of string’s default constructor162Types and DeclarationsChapter 6If you want initialization of local variables of built-in type or objects of built-in type created withnew, use {}.
For example:void ff(){int x {};char buf[1024]{};// x becomes 0// buf[i] becomes 0 for all iint∗ p {new int{10}};char∗ q {new char[1024]{}};// *p becomes 10// q[i] becomes 0 for all i// ...}A member of an array or a class is default initialized if the array or structure is.6.3.5.2 Initializer ListsSo far, we have considered the cases of no initializer and one initializer value.
More complicatedobjects can require more than one value as an initializer. This is primarily handled by initializerlists delimited by { and }. For example:int a[] = { 1, 2 };struct S { int x, string s };S s = { 1, "Helios" };complex<double> z = { 0, pi };vector<double> v = { 0.0, 1.1, 2.2, 3.3 };// array initializer// struct initializer// use constructor// use list constructorFor C-style initialization of arrays, see §7.3.1. For C-style structures, see §8.2. For user-definedtypes with constructors, see §2.3.2 or §16.2.5. For initializer-list constructors, see §17.3.4.In the cases above, the = is redundant. However, some prefer to add it to emphasize that a set ofvalues are used to initialize a set of member variables.In some cases, function-style argument lists can also be used (§2.3, §16.2.5).
For example:complex<double> z(0,pi);vector<double> v(10,3.3);// use constructor// use constructor : v gets 10 elements initialized to 3.3In a declaration, an empty pair of parentheses, (), always means ‘‘function’’ (§12.1). So, if youwant to be explicit about ‘‘use default initialization’’ you need {}. For example:complex<double> z1(1,2);complex<double> f1();// function-style initializer (initialization by constructor)// function declarationcomplex<double> z2 {1,2};complex<double> f2 {};// initialization by constructor to {1,2}// initialization by constructor to the default value {0,0}Note that initialization using the {} notation does not narrow (§6.3.5).When using auto, a {}-list has its type deduced to std::initializer_list<T>.
For example:auto x1 {1,2,3,4};// x1 is an initializer_list<int>auto x2 {1.0, 2.25, 3.5 }; // x2 is an initializer_list of<double>auto x3 {1.0,2};// error: cannot deduce the type of {1.0,2} (§6.3.6.2)Section 6.3.5.2Initializer Lists1636.3.6 Deducing a Type: auto and decltype()The language provides two mechanisms for deducing a type from an expression:• auto for deducing a type of an object from its initializer; the type can be the type of a variable, a const, or a constexpr.• decltype(expr) for deducing the type of something that is not a simple initializer, such as thereturn type for a function or the type of a class member.The deduction done here is very simple: auto and decltype() simply report the type of an expressionalready known to the compiler.6.3.6.1 The auto Type SpecifierWhen a declaration of a variable has an initializer, we don’t need to explicitly specify a type.Instead, we can let the variable have the type of its initializer.
Consider:int a1 = 123;char a2 = 123;auto a3 = 123; // the type of a3 is ‘‘int’’The type of the integer literal 123 is int, so a3 is an int. That is, auto is a placeholder for the type ofthe initializer.There is not much advantage in using auto instead of int for an expression as simple as 123. Theharder the type is to write and the harder the type is to know, the more useful auto becomes. Forexample:template<class T> void f1(vector<T>& arg){for (vector<T>::iterator p = arg.begin(); p!=arg.end(); ++p)∗p = 7;for (auto p = arg.begin(); p!=arg.end(); ++p)∗p = 7;}The loop using auto is the more convenient to write and the easier to read.