Can these two C ++ initializer syntax differ in semantics?

Suppose the following code is legal code that compiles correctly, that T is a type name, and x is a variable name.

The syntax is one:

 T a(x); 

Syntax two:

 T a = x; 

Does the exact semantics of the two expressions separate? If so, under what circumstances?

If these two expressions have different semantics, I am also very interested in what part of the standard speaks about this.

Also, if there is a special case where T is a name of a scalar type (aka, int , long , double , etc.), what are the differences when T is a scalar type compared to a non-scalar type?

+6
c ++ language-lawyer language-features initializer
source share
5 answers

Yes. If type x is not T , then the second example extends to T a = T(x) . This requires that T(T const&) be publicly available. The first example does not call the copy constructor.

After checking availability, the copy can be eliminated (as Tony pointed out). However, it cannot be resolved before checking availability.

+3
source share

From 8.5.14 (my attention):

The selected function is called with an initializer expression as an argument; if the function is a constructor, the call initializes the temporary destination type. The result of the call (which is temporary for the constructor) is then used to directly initialize, according to the above rules, an object that is the copy initialization destination. In some implementation cases , it is allowed to exclude copying inherent in this direct initialization by creating an intermediate result directly in the initialized object; see class.temporary, class.copy.

So are they equivalent for implementation.

8.5.11 is also relevant, but only in confirmation that there may be a difference:

-11- The form of initialization (using brackets or =) is usually insignificant, but it matters when the object being initialized has the class type; See below. An initializer in parentheses can only be a list of expressions when the initialized object has a class type.

+2
source share

The difference here is between implicit and explicit construction, and there may be a difference.

Imagine you have an Array type with an Array(size_t length) constructor, and elsewhere you have a count_elements(const Array& array) function. Their purpose is understandable, and the code seems readable enough until you understand that it will allow you to call count_elements(2000) . This is not only ugly code, but without any restrictions it allocates an array of 2000 elements in memory.

In addition, you may have other types that can be used implicitly for an integer, which allows you to also run count_elements (), which gives you completely useless results with high efficiency.

What you want to do here is declare Array(size_t length) an explicit constructor. This will disable implicit conversions, and Array a = 2000 will no longer be legal syntax.

This was just one example. Once you understand what the explicit keyword does, it's easy to dream of others.

+2
source share

T a(x) is direct initialization, and T a = x is copy initialization.

From the standard:

8.5.11 The form of initialization (using parentheses or =) is usually not significant, but it matters when the initialization of the object is of class type; See below. An initializer in parentheses can only be a list of expressions when the initialized object has a class type.

8.5.12 The initialization that occurs when passing arguments, returning a function, throwing an exception (15.1), handling the exception (15.3) and initializer lists with closed brackets (8.5.1) is called copy initialization and is equivalent to the form

  T x = a; 

The initialization that occurs in new expressions (5.3.4), static_cast expressions (5.2.9), type conversion of functional notation (5.2.3) and initializers of the basis and member (12.6.2) is called direct initialization and is equivalent to the form

  T x(a); 

The difference is that initializing the copy creates a temporary object, which is then used for direct initialization. The compiler is allowed to avoid creating a temporary object:

8.5.14 ... The result of the call (which is temporary for the constructor) is then used for direct initialization, according to the above rules, of the object, which is the copy initialization destination. In some implementation cases, it is allowed to exclude copying inherent in this direct initialization by creating an intermediate result directly in the initialized object; see 12.2, 12.8.

Initializing a copy requires an explicit constructor and copy constructor.

+2
source share

In C ++, when you write this:

 class A { public: A() { ... } }; 

The compiler really generates this, depending on what your code uses:

 class A { public: A() { ... } ~A() { ... } A(const A& other) {...} A& operator=(const A& other) { ... } }; 

So, now you can see the different semantics of various constructors.

 A a1; // default constructor A a2(a1); // copy constructor a2 = a1; // copy assignment operator 

Copy constructors basically copy all non-static data. They are generated only if the resulting code is legal and normal: if the compiler sees types inside the class that it does not know how to copy (for the usual assignment rules), then the copy constructor will not be generated. This means that if the type T does not support constructors or if one of the open fields of the class is const or a reference type, then, for example, the generator will not create them, and the code will not be created. Templates expand during build, so if the resulting code cannot be created, it will fail. And sometimes he fails out loud and very mysterious.

If you define a constructor (or destructor) in a class, the generator will not generate a default value. This means that you can override the default constructors. You can make them private (they are publicly accessible by default), you can redefine them so they don’t do anything (useful for preserving memory and preventing side effects), etc.

0
source share

All Articles