Do these two C++ initializer syntaxes ever differ in semantics? - c++

Assume that the following code is legal code that compiles properly, that T is a type name, and that x is the name of a variable.
Syntax one:
T a(x);
Syntax two:
T a = x;
Do the exact semantics of these two expressions ever differ? If so, under what circumstances?
If these two expressions ever do have different semantics I'm also really curious about which part of the standard talks about this.
Also, if there is a special case when T is the name of a scalar type (aka, int, long, double, etc...), what are the differences when T is a scalar type vs. a non-scalar type?

Yes. If the type of x is not T, then the second example expands to T a = T(x). This requires that T(T const&) is public. The first example doesn't invoke the copy constructor.
After the accessibility has been checked, the copy can be eliminated (as Tony pointed out). However, it cannot be eliminated before checking accessibility.

The difference here is between implicit and explicit construction, and there can be difference.
Imagine having a type Array with the constructor Array(size_t length), and that somewhere else, you have a function count_elements(const Array& array). The purpose of these are easily understandable, and the code seems readable enough, until you realise it will allow you to call count_elements(2000). This is not only ugly code, but will also allocate an array 2000 elements long in memory for no reason.
In addition, you may have other types that are implicitly castable to an integer, allowing you to run count_elements() on those too, giving you completely useless results at a high cost to efficiency.
What you want to do here, is declare the Array(size_t length) an explicit constructor. This will disable the implicit conversions, and Array a = 2000 will no longer be legal syntax.
This was only one example. Once you realise what the explicit keyword does, it is easy to dream up others.

From 8.5.14 (emphasis mine):
The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the destination type. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see class.temporary, class.copy.
So, whether they're equivalent is left to the implementation.
8.5.11 is also relevant, but only in confirming that there can be a difference:
-11- The form of initialization (using parentheses or =) is generally insignificant, but does matter when the entity being initialized has a class type; see below. A parenthesized initializer can be a list of expressions only when the entity being initialized has a class type.

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 generally insignificant, but does matter when the entity being initialized has a class type; see below. A parenthesized initializer can be a list of expressions only when the entity being initialized has a class type.
8.5.12 The initialization that occurs in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and brace-enclosed initializer lists (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), functional notation type conversions (5.2.3), and base and member initializers (12.6.2) is called direct-initialization and is equivalent to the form
T x(a);
The difference is that copy initialization creates a temporary object which is then used to direct-initialize. The compiler is allowed to avoid creating the temporary object:
8.5.14 ... The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see 12.2, 12.8.
Copy initialization requires a non-explicit constructor and a copy constructor to be available.

In C++, when you write this:
class A {
public:
A() { ... }
};
The compiler actually 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 the various constructors.
A a1; // default constructor
A a2(a1); // copy constructor
a2 = a1; // copy assignment operator
The copy constructors basically copy all the non-static data. They are only generated if the resulting code is legal and sane: if the compiler sees types inside the class that he doesn't know how to copy (per normal assignment rules), then the copy constructor won't get generated. This means that if the T type doesn't support constructors, or if one of the public fields of the class is const or a reference type, for instance, the generator won't create them - and the code won't build. Templates are expanded at build time, so if the resulting code isn't buildable, it'll fail. And sometimes it fails loudly and very cryptically.
If you define a constructor (or destructor) in a class, the generator won't generate a default one. This means you can override the default generated constructors. You can make them private (they're public by default), you can override them so they do nothing (useful for saving memory and avoiding side-effects), etc.

Related

Why the expression 'T foo = T()' is not copy initialized [duplicate]

Here is the little code snippet:
class A
{
public:
A(int value) : value_(value)
{
cout <<"Regular constructor" <<endl;
}
A(const A& other) : value_(other.value_)
{
cout <<"Copy constructor" <<endl;
}
private:
int value_;
};
int main()
{
A a = A(5);
}
I assumed that output would be "Regular Constructor" (for RHS) followed by "Copy constructor" for LHS. So I avoided this style and always declared variable of class as A a(5);. But to my surprise in the code above copy constructor is never called (Visual C++ 2008)
Does anybody know if this behavior is a result of compiler optimization, or some documented (and portable) feature of C++? Thanks.
From another comment: "So by default I should not rely on it (as it may depend on the compiler)"
No, it does not depend on the compiler, practically anyway. Any compiler worth a grain of sand won't waste time constructing an A, then copying it over.
In the standard it explicitly says that it is completely acceptable for T = x; to be equivalent to saying T(x);. (§12.8.15, pg. 211) Doing this with T(T(x)) is obviously redundant, so it removes the inner T.
To get the desired behavior, you'd force the compiler to default construct the first A:
A a;
// A is now a fully constructed object,
// so it can't call constructors again:
a = A(5);
I was researching this to answer another question that was closed as a dupe, so in order to not let the work go to waste I 'm answering this one instead.
A statement of the form A a = A(5) is called copy-initialization of the variable a. The C++11 standard, 8.5/16 states:
The function selected is called with the initializer expression as
its argument; if the function is a constructor, the call initializes a
temporary of the cv-unqualified version of the destination type. The
temporary is a prvalue. The result of the call (which is the temporary
for the constructor case) is then used to direct-initialize, according
to the rules above, the object that is the destination of the
copy-initialization. In certain cases, an implementation is permitted
to eliminate the copying inherent in this direct-initialization by
constructing the intermediate result directly into the object being
initialized; see 12.2, 12.8.
This means that the compiler looks up the appropriate constructor to handle A(5), creates a temporary and copies that temporary into a. But under what circumstances can the copy be eliminated?
Let's see what 12.8/31 says:
When certain criteria are met, an implementation is allowed to omit
the copy/move construction of a class object, even if the copy/move
constructor and/or destructor for the object have side effects. In
such cases, the implementation treats the source and target of the
omitted copy/move operation as simply two different ways of referring
to the same object, and the destruction of that object occurs at the
later of the times when the two objects would have been destroyed
without the optimization. This elision of copy/move operations,
called copy elision, is permitted in the following circumstances
(which may be combined to eliminate multiple copies):
[...]
when a temporary class object that has not been bound to a reference (12.2) would be copied/moved
to a class object with the same cv-unqualified type, the copy/move operation can be
omitted by constructing the temporary object directly into the target of the omitted copy/move
Having all this in mind, here's what happens with the expression A a = A(5):
The compiler sees a declaration with copy-initialization
The A(int) constructor is selected to initialize a temporary object
Because the temporary object is not bound to a reference, and it does have the same type A as the destination type in the copy-initialization expression, the compiler is permitted to directly construct an object into a, eliding the temporary
Here you have copy-initialization of a from temporary A(5). Implementation allowed to skip calling copy constructor here according to C++ Standard 12.2/2.
A a = A(5);
This line is equivalent to
A a(5);
Despite its function-style appearance, the first line simply constructs a with the argument 5. No copying or temporaries are involved. From the C++ standard, section 12.1.11:
A functional notation type conversion (5.2.3) can be used to create new objects of its type. [ Note: The
syntax looks like an explicit call of the constructor. —end note ]

Confusion in copy initialization and direct initialization

Consider simple statement (Taken from Is there a difference in C++ between copy initialization and direct initialization?):
A c2 = A();
This statement value-initializes a temporary and then copies that
value into c2 (Read 5.2.3/2 and 8.5/14). This of course will require a
non-explicit copy constructor (Read 8.5/14 and 12.3.1/3 and
13.3.1.3/1)
[Mind the bold sentence in above para] -> My question is why?
Now consider this code :
class B {};
struct A
{
A(B const&) {}
A(A const&) = delete;
//A(A const&); //delete above statement and uncomment this statement,
//and everything works, even though there in no body of copy constructor Oo
};
A a2 = B(); //error since there is no copy constructor oO
Why copy-initialization requires presence of copy constructor even though it's not needed sometime as presented in above code
Please please one more thing :
While direct initialization has all constructors available to call,
and in addition can do any implicit conversion it needs to match up
argument types, copy initialization can just set up one implicit
conversion sequence.
[Mind the bolding in the following para]
Doesn't that means direct initialization have access to all constructors and can perform implicit conversion sequence , while copy initialization all can do is perform implicit conversion sequence? . What I mean to ask is , implicit conversion in direct initialization is different from implicit conversion sequence in copy initialization ?
The rules for evaluating
A a1 = B(); // (1) copy-initialization
A a2 = {B()}; // (2) copy-initialization
A a3{B()}; // (3) direct-initialization
come from [dcl.init]/17:
— If the initializer is a (non-parenthesized) braced-init-list, the object or reference is list-initialized (8.5.4).
— [...]
— If the destination type is a (possibly cv-qualified) class type:
If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified
version of the source type is the same class as, or a derived class of, the class of the destination,
constructors are considered. [...]
Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences
that can convert from the source type to the destination type or (when a conversion function
is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is
chosen through overload resolution (13.3). [...] The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above,
the object that is the destination of the copy-initialization. In certain cases, an implementation
is permitted to eliminate the copying inherent in this direct-initialization by constructing the
intermediate result directly into the object being initialized; see 12.2, 12.8.
For both a2 and a3, the initializer is a braced-init-list, so we just do list-initialization. This ends up calling the B const& constructor.
For a1, the first sub-bullet doesn't apply - since the source type (B) is not the same or derived class of the destination type (A). So we go into the second sub-bullet point which involves considering conversion functions. There is one (A(B const&)) so we effectively rewrite the expression
A a1_new{A{B{}}};
Now typically, this extra copy will be elided. But you're explicitly prohibiting it, so the code cannot compile.
As to why the differentiation? I don't know. It seems like copy-initialization should simply be syntactic sugar for direct-initialization. In most cases, after all, it is...
(The following applies to C++11)To avoid rambling too much, A a2 = B(); has two stage : first, A temp object of type B is created when you write B(). Then, your function A(B const&) {} will not be invoked here with the role of directly initializing A because you use the copy-initialization syntax. If you want to invoke it to directly initialize A, you should write A a2(B()) instead, rather than using the copy-initialization syntax(the devil here is =). So, what's next? You get a temp object of type B, and you want to use it to initialize obj of type A, and now, you indirectly initialize A by converting B() to the type A. So your function A(B const&) {} is used as a type conversion rather than directly initializing A.
Because of conversion, a temp obj of type A is created, and then we need copy constructor to initialize a2 using that temporary obj.
In copy initialization, type-conversion-functions with explicit keyword can't be called. The design philosophy of explicit keyword is that you should directly invoke it. Combined with type-conversion-functions, those explicit attributed functions should not be used for implicit type conversion. In direct initialization, they can all be called even they are explicit.
To make things interesting, if you make your copy constructor and move constructor explicit, you can't even write T obj = T{}, but you can write T obj {T{}}. You can view copy and move ctor as a conversion too, only the destination and source type are of the same class.
I would like to provide you with further readings here. After that, read this question to know about copy-list-initialization and this question to learn about aggregate-types.

Why only call constructor one time? [duplicate]

Here is the little code snippet:
class A
{
public:
A(int value) : value_(value)
{
cout <<"Regular constructor" <<endl;
}
A(const A& other) : value_(other.value_)
{
cout <<"Copy constructor" <<endl;
}
private:
int value_;
};
int main()
{
A a = A(5);
}
I assumed that output would be "Regular Constructor" (for RHS) followed by "Copy constructor" for LHS. So I avoided this style and always declared variable of class as A a(5);. But to my surprise in the code above copy constructor is never called (Visual C++ 2008)
Does anybody know if this behavior is a result of compiler optimization, or some documented (and portable) feature of C++? Thanks.
From another comment: "So by default I should not rely on it (as it may depend on the compiler)"
No, it does not depend on the compiler, practically anyway. Any compiler worth a grain of sand won't waste time constructing an A, then copying it over.
In the standard it explicitly says that it is completely acceptable for T = x; to be equivalent to saying T(x);. (§12.8.15, pg. 211) Doing this with T(T(x)) is obviously redundant, so it removes the inner T.
To get the desired behavior, you'd force the compiler to default construct the first A:
A a;
// A is now a fully constructed object,
// so it can't call constructors again:
a = A(5);
I was researching this to answer another question that was closed as a dupe, so in order to not let the work go to waste I 'm answering this one instead.
A statement of the form A a = A(5) is called copy-initialization of the variable a. The C++11 standard, 8.5/16 states:
The function selected is called with the initializer expression as
its argument; if the function is a constructor, the call initializes a
temporary of the cv-unqualified version of the destination type. The
temporary is a prvalue. The result of the call (which is the temporary
for the constructor case) is then used to direct-initialize, according
to the rules above, the object that is the destination of the
copy-initialization. In certain cases, an implementation is permitted
to eliminate the copying inherent in this direct-initialization by
constructing the intermediate result directly into the object being
initialized; see 12.2, 12.8.
This means that the compiler looks up the appropriate constructor to handle A(5), creates a temporary and copies that temporary into a. But under what circumstances can the copy be eliminated?
Let's see what 12.8/31 says:
When certain criteria are met, an implementation is allowed to omit
the copy/move construction of a class object, even if the copy/move
constructor and/or destructor for the object have side effects. In
such cases, the implementation treats the source and target of the
omitted copy/move operation as simply two different ways of referring
to the same object, and the destruction of that object occurs at the
later of the times when the two objects would have been destroyed
without the optimization. This elision of copy/move operations,
called copy elision, is permitted in the following circumstances
(which may be combined to eliminate multiple copies):
[...]
when a temporary class object that has not been bound to a reference (12.2) would be copied/moved
to a class object with the same cv-unqualified type, the copy/move operation can be
omitted by constructing the temporary object directly into the target of the omitted copy/move
Having all this in mind, here's what happens with the expression A a = A(5):
The compiler sees a declaration with copy-initialization
The A(int) constructor is selected to initialize a temporary object
Because the temporary object is not bound to a reference, and it does have the same type A as the destination type in the copy-initialization expression, the compiler is permitted to directly construct an object into a, eliding the temporary
Here you have copy-initialization of a from temporary A(5). Implementation allowed to skip calling copy constructor here according to C++ Standard 12.2/2.
A a = A(5);
This line is equivalent to
A a(5);
Despite its function-style appearance, the first line simply constructs a with the argument 5. No copying or temporaries are involved. From the C++ standard, section 12.1.11:
A functional notation type conversion (5.2.3) can be used to create new objects of its type. [ Note: The
syntax looks like an explicit call of the constructor. —end note ]

initializing a non-copyable member (or other object) in-place from a factory function

A class must have a valid copy or move constructor for any of this syntax to be legal:
C x = factory();
C y( factory() );
C z{ factory() };
In C++03 it was fairly common to rely on copy elision to prevent the compiler from touching the copy constructor. Every class has a valid copy constructor signature regardless of whether a definition exists.
In C++11 a non-copyable type should define C( C const & ) = delete;, rendering any reference to the function invalid regardless of use (same for non-moveable). (C++11 §8.4.3/2). GCC, for one, will complain when trying to return such an object by value. Copy elision ceases to help.
Fortunately, we also have new syntax to express intent instead of relying on a loophole. The factory function can return a braced-init-list to construct the result temporary in-place:
C factory() {
return { arg1, 2, "arg3" }; // calls C::C( whatever ), no copy
}
Edit: If there's any doubt, this return statement is parsed as follows:
6.6.3/2: "A return statement with a braced-init-list initializes the object or reference to be returned from the function by copy-list-initialization (8.5.4) from the specified initializer list."
8.5.4/1: "list-initialization in a copy-initialization context is called copy-list-initialization." ¶3: "if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7)."
Do not be misled by the name copy-list-initialization. 8.5:
13: The form of initialization (using parentheses or =) is generally insignificant, but does matter when the
initializer or the entity being initialized has a class type; see below. If the entity being initialized does not
have class type, the expression-list in a parenthesized initializer shall be a single expression.
14: The initialization that occurs in the form
T x = a;
as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called copy-initialization.
Both copy-initialization and its alternative, direct-initialization, always defer to list-initialization when the initializer is a braced-init-list. There is no semantic effect in adding the =, which is one reason list-initialization is informally called uniform initialization.
There are differences: direct-initialization may invoke an explicit constructor, unlike copy-initialization. Copy-initialization initializes a temporary and copies it to initialize the object, when converting.
The specification of copy-list-initialization for return { list } statements merely specifies the exact equivalent syntax to be temp T = { list };, where = denotes copy-initialization. It does not immediately imply that a copy constructor is invoked.
-- End edit.
The function result can then be received into an rvalue reference to prevent copying the temporary to a local:
C && x = factory(); // applies to other initialization syntax
The question is, how to initialize a nonstatic member from a factory function returning non-copyable, non-moveable type? The reference trick doesn't work because a reference member doesn't extend the lifetime of a temporary.
Note, I'm not considering aggregate-initialization. This is about defining a constructor.
On your main question:
The question is, how to initialize a nonstatic member from a factory function returning non-copyable, non-moveable type?
You don't.
Your problem is that you are trying to conflate two things: how the return value is generated and how the return value is used at the call site. These two things don't connect to each other. Remember: the definition of a function cannot affect how it is used (in terms of language), since that definition is not necessarily available to the compiler. Therefore, C++ does not allow the way the return value was generated to affect anything (outside of elision, which is an optimization, not a language requirement).
To put it another way, this:
C c = {...};
Is different from this:
C c = [&]() -> C {return {...};}()
You have a function which returns a type by value. It is returning a prvalue expression of type C. If you want to store this value, thus giving it a name, you have exactly two options:
Store it as a const& or &&. This will extend the lifetime of the temporary to the lifetime of the control block. You can't do that with member variables; it can only be done with automatic variables in functions.
Copy/move it into a value. You can do this with a member variable, but it obviously requires the type to be copyable or moveable.
These are the only options C++ makes available to you if you want to store a prvalue expression. So you can either make the type moveable or return a freshly allocated pointer to memory and store that instead of a value.
This limitation is a big part of the reason why moving was created in the first place: to be able to pass things by value and avoid expensive copies. The language couldn't be changed to force elision of return values. So instead, they reduced the cost in many cases.
Issues like this were among the prime motivations for the change in C++17 to allow these initializations (and exclude the copies from the language, not merely as an optimization).

Why copy constructor is not called in this case?

Here is the little code snippet:
class A
{
public:
A(int value) : value_(value)
{
cout <<"Regular constructor" <<endl;
}
A(const A& other) : value_(other.value_)
{
cout <<"Copy constructor" <<endl;
}
private:
int value_;
};
int main()
{
A a = A(5);
}
I assumed that output would be "Regular Constructor" (for RHS) followed by "Copy constructor" for LHS. So I avoided this style and always declared variable of class as A a(5);. But to my surprise in the code above copy constructor is never called (Visual C++ 2008)
Does anybody know if this behavior is a result of compiler optimization, or some documented (and portable) feature of C++? Thanks.
From another comment: "So by default I should not rely on it (as it may depend on the compiler)"
No, it does not depend on the compiler, practically anyway. Any compiler worth a grain of sand won't waste time constructing an A, then copying it over.
In the standard it explicitly says that it is completely acceptable for T = x; to be equivalent to saying T(x);. (§12.8.15, pg. 211) Doing this with T(T(x)) is obviously redundant, so it removes the inner T.
To get the desired behavior, you'd force the compiler to default construct the first A:
A a;
// A is now a fully constructed object,
// so it can't call constructors again:
a = A(5);
I was researching this to answer another question that was closed as a dupe, so in order to not let the work go to waste I 'm answering this one instead.
A statement of the form A a = A(5) is called copy-initialization of the variable a. The C++11 standard, 8.5/16 states:
The function selected is called with the initializer expression as
its argument; if the function is a constructor, the call initializes a
temporary of the cv-unqualified version of the destination type. The
temporary is a prvalue. The result of the call (which is the temporary
for the constructor case) is then used to direct-initialize, according
to the rules above, the object that is the destination of the
copy-initialization. In certain cases, an implementation is permitted
to eliminate the copying inherent in this direct-initialization by
constructing the intermediate result directly into the object being
initialized; see 12.2, 12.8.
This means that the compiler looks up the appropriate constructor to handle A(5), creates a temporary and copies that temporary into a. But under what circumstances can the copy be eliminated?
Let's see what 12.8/31 says:
When certain criteria are met, an implementation is allowed to omit
the copy/move construction of a class object, even if the copy/move
constructor and/or destructor for the object have side effects. In
such cases, the implementation treats the source and target of the
omitted copy/move operation as simply two different ways of referring
to the same object, and the destruction of that object occurs at the
later of the times when the two objects would have been destroyed
without the optimization. This elision of copy/move operations,
called copy elision, is permitted in the following circumstances
(which may be combined to eliminate multiple copies):
[...]
when a temporary class object that has not been bound to a reference (12.2) would be copied/moved
to a class object with the same cv-unqualified type, the copy/move operation can be
omitted by constructing the temporary object directly into the target of the omitted copy/move
Having all this in mind, here's what happens with the expression A a = A(5):
The compiler sees a declaration with copy-initialization
The A(int) constructor is selected to initialize a temporary object
Because the temporary object is not bound to a reference, and it does have the same type A as the destination type in the copy-initialization expression, the compiler is permitted to directly construct an object into a, eliding the temporary
Here you have copy-initialization of a from temporary A(5). Implementation allowed to skip calling copy constructor here according to C++ Standard 12.2/2.
A a = A(5);
This line is equivalent to
A a(5);
Despite its function-style appearance, the first line simply constructs a with the argument 5. No copying or temporaries are involved. From the C++ standard, section 12.1.11:
A functional notation type conversion (5.2.3) can be used to create new objects of its type. [ Note: The
syntax looks like an explicit call of the constructor. —end note ]