'Bracket initializing'. (C++) - c++

I'm learning C++ at the moment, C++ Primer plus. But I just felt like checking out the cplusplus website and skip a little forward to file handling.
I pretty much know the basics of file handling coming from java, php, visual basic. But I came across a pretty weird line.
ostream os(&fb);
fb represents a filebuf. I just don't get the syntax of this, but I can figure out that it's the same as:
ostream os = &fb;
But I never really read about this way of initializing variables.
So I'm wondering. Am I just senseless and missing out a real useful feature the entire time? Is this way of initializing just old? Is it something different?
Thanks in advance.

Both forms perform initialization. The first syntax (with ()) is called direct-initialization syntax. The second syntax (with =) is called copy-initialization syntax. They will act same in most real-life cases, but there are indeed differences between the two.
In situations when types on the left-hand side (LHS) and right-hand side (RHS) are identical (ignoring any const/volatile qualifiers), both are indeed exactly the same. The language standard explicitly states that in this case the = form is equivalent to () form.
But when the types are different (and the LHS type is a class type), these two forms will generally work differently.
The copy-initialization form works as follows: convert the RHS value to the temporary object of LHS type (by any means possible: standard conversion, conversion operator, conversion constructor). And then use the copy constructor of the LHS class to copy the temporary object to the LHS object.
The direct initialization form work as follows: just consider all constructors of LHS and choose the most appropriate one by using overload resolution.
You can immediately notice that the copy-initialization syntax unconditionally uses the copy constructor (the copying and the intermediate temporary can be optimized away, but conceptually they are there). If the LHS class has no accessible copy-constructor, the copy-initialization unconditionally becomes ill-formed, while direct-initialization might still work.
Also, the keyword explicit applied to certain constructor will affect which form of initialization is available for which combinations of types.

A small program to see when copy constructor is called and when overloaded assignment operator function is called:
#include <iostream>
using namespace std;
class test
{
public:
// default constructor.
test()
{
cout<<"Default Ctor called"<<endl;
}
// copy constructor.
test(const test& other)
{
cout<<"Copy Ctor called"<<endl;
}
// overloaded assignment operator function.
test& operator=(const test& other)
{
cout<<"Overload operator function called"<<endl;
return *this;
}
};
int main(void)
{
test obj1; // default constructor called.
test obj2 = obj1; // copy constructor called.
test obj3(obj2); // again copy constructor called.
obj1 = obj2; // overloaded assignment operator function.
return 0;
}
Output:
Default Ctor called
Copy Ctor called
Copy Ctor called
Overload operator function called
So in your case, the copy constructor of ostream is called in both the occasions.

Perhaps you should read this and this

One important benefit of function-call initialization is that they also work with constructors that take multiple arguments. For example, an fstream constructor can take two parameters:
std::fstream file("filename", ios_base::out);
Until C++0x uniform initialization is widely available, function-call initialization is the only way to handle multiple argument constructors.

From my understanding, &var is a alias for the var variable and doesn't matter which one you use.
--------Addition -----------------
The below code is taken from Stroustrup book.From this its clear that both are alias to the same variable. It also says as below.
"The semantics of argument passing are defined to be those of initialization, so when called, increment's argument aa became another name of x." Thats why I refered to &x to be alias of x.
void increment(int& aa) { aa++; }
void f()
{
int x = 1;
increment(x);
}

Related

Why is copy assigment possible, if a class has only a (templated) move assignment operator?

I have stumbled over code today, that I don't understand. Please consider the following example:
#include <iostream>
#include <string>
class A
{
public:
template <class Type>
Type& operator=(Type&& theOther)
{
text = std::forward<Type>(theOther).text;
return *this;
}
private:
std::string text;
};
class B
{
public:
B& operator=(B&& theOther)
{
text = std::forward<B>(theOther).text;
return *this;
}
private:
std::string text;
};
int main()
{
A a1;
A a2;
a2 = a1;
B b1;
B b2;
b2 = b1;
return 0;
}
When compiling, MinGW-w64/g++ 10.2 states:
..\src\Main.cpp: In function 'int main()':
..\src\Main.cpp:41:7: error: use of deleted function 'B& B::operator=(const B&)'
41 | b2 = b1;
| ^~
..\src\Main.cpp:19:7: note: 'B& B::operator=(const B&)' is implicitly declared as deleted because 'B' declares a move constructor or move assignment operator
19 | class B
| ^
mingw32-make: *** [Makefile:419: Main.o] Error 1
I fully understand the error message. But I don't understand why I don't get the same message with class A. Isn't the templated move assignment operator also a move assignment operator? Why then is the copy assignment operator not deleted? Is this well-written code?
Isn't the templated move assignment operator also a move assignment operator?
No, it's not considered as move assignment operator.
(emphasis mine)
A move assignment operator of class T is a non-template non-static member function with the name operator= that takes exactly one parameter of type T&&, const T&&, volatile T&&, or const volatile T&&.
As the effect, A still has the implicitly-declared copy/move assignment operator.
BTW: Your template assignment operator takes forwarding reference, it could accept both lvalue and rvalue. In a2 = a1;, it wins against the generated copy assignment operator in overload resolution and gets called.
As complement to #songyuanyao's standard-based answer: these kind of language rules are common reasons for guidelines such as MISRA/AUTOSAR (language guidelines for safety-critical C++ development) to have "avoid developer confusion" rules such as:
(from AUTOSAR C++14 Guidelines)
Rule A14-5-1 (required, implementation, automated)
A template constructor shall not participate in overload resolution
for a single argument of the enclosing class type.
Rationale
A template constructor is never a copy or move constructor and
therefore doesn’t prevent the implicit definition of a copy or move
constructor even if the template constructor looks similar and might
easily be confused. At the same time, copy or move operations do not
necessarily only use a copy or move constructor, but go through the
normal overload resolution process to find the best matching function
to use. This can cause confusion in the following cases:
a template constructor that looks like a copy/move constructor is not selected
for a copy/move operation because the compiler has generated an implicit copy/move constructor as well a template constructor is
selected in preference over a copy/move constructor because the
template constructor is a better match
To avoid these confusing situations, template constructors shall not
participate in overload resolution for a single argument of the
enclosing class type to avoid a template constructor being selected
for a copy/move operation. It also makes it clear that the constructor
is not a copy/move constructor and that it does not prevent the
implicit generation of copy/move constructors.
Rule M14-5-3 (required, implementation, automated)
A copy assignment operator shall be declared when there is a template
assignment operator with a parameter that is a generic parameter.
Namely that it can be surprising for developers that a template copy/move ctor/assignment operator does not suppress (/does not "activate" rule of 5) implicitly-generated ones. You are typically required to use SFINAE to make sure that the templated ctor/assignment op does not act as if it was a copy/move ctor/assignment by allowing the overload to be active for a single argument of the enclosing class.

std::optional: Not participating in overload resolution vs. being defined as deleted

I am trying to understand the mechanism behind type traits propagation as described for std::optional in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0602r4.html. There is a subtle difference in the treatment of copy operations, which shall be conditionally defined as deleted, versus move operations, which shall rather not participate in overload resolution.
What is the reason for that difference, and how would I test the latter? Example:
#include <type_traits>
#include <optional>
struct NonMoveable {
NonMoveable() = default;
NonMoveable(NonMoveable const&) = default;
NonMoveable(NonMoveable&&) = delete;
NonMoveable& operator=(NonMoveable const&) = default;
NonMoveable& operator=(NonMoveable&&) = delete;
};
// Inner traits as expected
static_assert(!std::is_move_constructible<NonMoveable>::value);
static_assert(!std::is_move_assignable<NonMoveable>::value);
// The wrapper is moveable, via copy operations participating in
// overload resolution. How to verify that the move operations don't?
static_assert(std::is_move_constructible<std::optional<NonMoveable>>::value);
static_assert(std::is_move_assignable<std::optional<NonMoveable>>::value);
int main(int argc, char* argv[])
{
NonMoveable a1;
NonMoveable a2{std::move(a1)}; // Bad, as expected
std::optional<NonMoveable> b1;
std::optional<NonMoveable> b2{std::move(b1)}; // Good, see above. But
// useless as a test for
// P0602R4.
return 0;
}
Bonus Question
Does GCC do the right thing? I have modified the example a bit to get a tiny step closer: https://godbolt.org/z/br1vx1. Here I made the copy operations inaccessible by declaring them private. GCC-10.2 with -std=c++20 now fails the static asserts and complains
error: use of deleted function 'std::optional<NonMoveable>::optional(std::optional<NonMoveable>&&)'
According to Why do C++11-deleted functions participate in overload resolution? the delete is applied after overload resolution, which could indicate that the move constructor participated, despite P0602R4 said it shall not.
On the other hand https://en.cppreference.com/w/cpp/language/overload_resolution states right in the beginning
... If these steps produce more than one candidate function, then overload resolution is performed ...
so overload resolution was skipped, because the move constructor was the only candidate?
std::optional is a red herring; the key is understanding the mechanisms that lead to why these requirements are placed on a library type
There is a subtle difference in the treatment of copy operations, which shall be conditionally defined as deleted, versus move operations, which shall rather not participate in overload resolution.
The under-the-hood requirements (and how to implement these) for std::optional are complex. However, the requirement that move operations shall not participate in overload resolution (for non-movable types) vs copy operations being deleted (for non-copyable types) likely relates to a separate topic;
NRVO(1) (a case of copy elision, if you may) and
more implicit moves, e.g. choosing move constructors over copy constructors when returning named objects with automatic storage duration from a function.
We can understand this topic by looking at simpler types than std::optional.
(1) Named Returned Value Optimization
TLDR
The move eagerness that is expanding in C++ (more eager moves in C++20) means there are special cases where a move constructor will be chosen over a copy constructor even if the move constructor has been deleted. The only way to avoid these for, say, non-movable types, is to make sure the type has no move constructor nor a move assignment operator, by knowledge of the rule of 5 and what governs whether these are defined implicitly.
The same preference does not exist for copying, and there would be no reasons to favour removing these over deleting them, if this was even possible (2). In other words, the same kinks that exist for favouring moves in overload resolution, that sometimes unexpectedly choose move over copy, is not present for the reverse; copy over move.
(2) There is no such thing as a class without the existence of a copy ctor and a copy assignment operator (although these may be defined as deleted).
Implicit move eagerness
Consider the following types:
struct A {
A() { std::cout << __PRETTY_FUNCTION__ << "\n"; }
A(A const &) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
A &operator=(A const &) {
std::cout << __PRETTY_FUNCTION__ << "\n";
return *this;
}
};
struct B {
B() { std::cout << __PRETTY_FUNCTION__ << "\n"; }
B(B const &) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
B &operator=(B const &) {
std::cout << __PRETTY_FUNCTION__ << "\n";
return *this;
}
B(B &&) = delete;
B &operator=(B &&) = delete;
};
Where, A:
has user-defined constructors, such that a move constructor and move assigment operator will not be implicitly defined,
and where B, moreover:
declares and deletes a move constructor and a move assignment operator; as these are declared and defined as deleted, they will participate in overload resolution.
Before we continue through different standard versions, we define the following functions that we shall return to:
A getA() {
A a{};
return a;
}
B getB() {
B b{};
return b;
}
C++14
Now, in C++14 an implementation was allowed to implement copy(/move) elision for certain scenarios; citing [class.copy]/31 from N4140 (C++14 + editorial fixes) [emphasis mine]:
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects. [...]
This elision of copy/move operations, called copy elision, is permitted in the following circumstances:
in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function's return value
[...]
and, from [class.copy]/32 [emphasis mine]:
When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.
But [class.temporary]/1 still placed the same semantic restrictions on an elided copy of an object as if the copy had actually not been elided [emphasis mine]
[..] Even when the creation of the temporary object is unevaluated (Clause [expr]) or otherwise avoided ([class.copy]), all the semantic restrictions shall be respected as if the temporary object had been created and later destroyed.
Such that, even for a situation where copy elision was eligible (and performed), the conversion sequences from, say, a named object viable for NRVO, would need to go through overload resolution to find (possibly elided) converting constructors, and would start with a pass through overload resolution as if the object were designated by an rvalue. This means, that in C++14, the following was well-formed
auto aa{getA()}; // OK, and copy most likely elided.
whereas the following was ill-formed:
auto bb{getB()}; // error: use of deleted function 'B::B(B&&)'
as overload resolution would find the declared but deleted move constructor of B during the step of considering b in return b; in getB() as an rvalue. For A, no move constructor exists, meaning overload resolution for a in return a; in getA() with a as an rvalue would fail, and whereafter overload resolution without this kink would succeed in finding the copy constructor of A (which would subsequently be elided).
C++17
Now, in C++17 copy elision was made stronger by the concept of delayed (end entirely elided) materialization of temporaries, particularly adding [class.temporary]/3 [emphasis mine]:
When an object of class type X is passed to or returned from a function, if each copy constructor, move constructor, and destructor of X is either trivial or deleted, and X has at least one non-deleted copy or move constructor, implementations are permitted to create a temporary object to hold the function parameter or result object. The temporary object is constructed from the function argument or return value, respectively, and the function's parameter or return object is initialized as if by using the non-deleted trivial constructor to copy the temporary (even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object).
This makes a large difference, as copy elision can now be performed for getB() without passing through the special rules of return value overload resolution (which previously picked the deleted move constructor), such that both of these are well-formed in C++17:
auto aa(getA()); // OK, copy elided.
auto bb(getB()); // OK, copy elided.
C++20
C++20 implements P1825R0 which allows even more implicit moves, expanding the cases where move construction or assignment may take place even when one would, at first glance, expect a copy construction/assignment (possible elided).
Summary
The quite complex rules with regard to move eagerness (over copying) can have some unexpected effects, and if a designer wants to make sure a type will not run into a corner case where a deleted move constructor or move assignment operator takes precedence in overload resolution over an non-deleted copy constructor or copy assignment operator, it is better to make sure that there are no move ctor/assignment operator available for overload resolution to find (for these cases), as compared to declaring them and defining them as explicitly-deleted. This argument does not apply for the move ctor/copy assignment operator however, as:
the standard contains no similar copy-eagerness (over move), and
there is no such thing as a class without a copy constructor or copy assignment operator, and removing these from overload resolution is basically(3) only possible in C++20 using requires-clauses.
As an example (and probably a GCC regression bug) of the difficulty of getting these rules right for a non-language lawyer, GCC trunk currently rejects the following program for C++20 (DEMO):
// B as above
B getB() {
B b{};
return b;
}
with the error message
error: use of deleted function 'B::B(B&&)'
In this case, one would expect a copy (possibly elided) to be chosen above in case B had deleted its move ctor. When in doubt, make sure the move ctor and assignment operator don't participate (i.e., exist) in overload resolution.
(3) One could declare a deleted assignment operator overloaded with both const- and ref-qualifiers, say const A& operator=(const A&) const && = delete;, which would very seldom be a viable candidate during overload solution (assignment to const rvalue), and which would guarantee the non-existence of the other non-const and &-qualified overloads that would otherwise likely to be valid overload candidates.

C++ implicit conversions with brace initializers

I've recently read somewhere (can't remember where) about using braces to allow multiple user-defined conversions, but there seems to be a difference between conversion by constructor and conversion by conversion method that I don't understand.
Consider:
#include <string>
using ::std::string;
struct C {
C() {}
};
struct A {
A(const string& s) {} // Make std::string convertible to A.
operator C() const { return C(); } // Makes A convertible to C.
};
struct B {
B() {}
B(const A& a) {} // Makes A convertible to B.
};
int main() {
B b;
C c;
// This works.
// Conversion chain (all thru ctors): char* -> string -> A -> B
b = {{"char *"}};
// These two attempts to make the final conversion through A's
// conversion method yield compiler errors.
c = {{"char *"}};
c = {{{"char *"}}};
// On the other hand, this does work (not surprisingly).
c = A{"char *"};
}
Now, I may be misinterpreting what the compiler is doing, but (based on the above and additional experimentation) it seems to me that it's not considering conversions by conversion-method. Reading through Sections 4 and 13.3.3.1 of the standard, however, I wasn't able to find a clue why this is. What is the explanation?
Update
Here's another interesting phenomenon I'd like explained. If I add
struct D {
void operator<<(const B& b) {}
};
and in main:
D d;
d << {{ "char *" }};
I get an error, but if instead I write d.operator<<({{ "char *" }}); it works fine.
Update 2
Looks like Section 8.5.4 in the standard may hold some answers. I'll report my findings.
There is one user conversion possible.
In b = {{"char *"}};
we actually do
b = B{{"char*"}}; // B has constructor with A (and a copy constructor not viable here)
so
b = B{A{"char*"}}; // One implicit conversion const char* -> std::string
in c = {{"const char*"}}, we try
c = C{{"char *"}}; // but nothing to construct here.
Digging through Section 8.5.4 of the standard and following various cross references therein, I think I understand what's going on. Of course, IANAL, so I might be wrong; this is my best effort.
Update: The previous version of the answer actually used multiple conversions. I've updated it to reflect my current understanding.
The key to unraveling the mess is the fact that a braced-init-list is not an expression (which also explains why d << {{"char *"}} won't compile). What it is is special syntax, governed by special rules, that is allowed in a number of specific contexts. Of these contexts, the relevant ones for our discussion are: rhs of assignment, argument in a function call, and argument in a constructor invocation.
So what happens when the compiler sees b = {{"char *"}}? This is a case of rhs of assignment. The applicable rule is:
A braced-init-list may appear on the right-hand side of ... an assignment defined by a user-defined assignment operator, in which case the initializer list is passed
as the argument to the operator function.
(Presumably, the default copy assignment operator is considered a user-defined assignment operator. I couldn't find a definition of that term anywhere, and there doesn't seem to be any language allowing the brace syntax specifically for default copy assignment.)
So we are reduced to argument passing to the default copy assignment operator B::operator=(const B&), where the argument being passed is {{"char *"}}. Because a braced-init-list is not an expression, there is no issue of conversion here, but rather a form of initialization of a temporary of type B, specifically, so called list initialization.
If no viable initializer-list constructor is found, overload resolution is performed again, where the
candidate functions are all the constructors of the class T and the argument list consists of the elements
of the initializer list.
So the compiler strips off the outer pair of braces and performs overload resolution using {"char *"} as the argument. This succeeds, matching the constructor B::B(const A&) because there is again list initialization of a temporary of type A in which overload resolution succeeds to match A::A(const string&) for the argument "char *", which is possible through the one alloted user-defined conversion, namely, from char* to string.
Now, in the case of c = {{"char *"}} the process is similar, but when we try to list-initialize a temporary of type C with {{"char *"}}, overload resolution fails to find a constructor that matches. The point is that by definition list-initialization only works through a constructor whose parameter list can be made to match the contents of the list.

Which constructor is invoked here?

In this code fragment, which constructor is actually called?
Vector v = getVector();
Vector has copy constructor, default constructor and assignment operator:
class Vector {
public:
...
Vector();
Vector(const Vector& other);
Vector& operator=(const Vector& other);
};
getVector returns by value.
Vector getVector();
Code uses C++03 standard.
Code fragment looks like it is supposed to call default constructor and then assignment operator, but I suspect that this declaration is another form of using copy constructor. Which is correct?
When = appears in an initialization, it calls the copy constructor. The general form is not exactly the same as calling the copy constructor directly though. In the statement T a = expr;, what happens is that if expr is of type T, the copy constructor is called. If expr is not of type T, then first an implicit conversion is done, if possible, then the copy constructor is called with that as an argument. If an implicit conversion is not possible, then the code is ill-formed.
Depending upon how getVector() is structured, the copy may be optimized away, and the object that was created inside the function is the same physical object that gets stored in v.
Assuming that you haven't done something pathological outside of the code you are showing, your declaration is a copy-initialization, and the second part of this rule applies:
13.3.1.3 Initialization by constructor [over.match.ctor]
1 When objects of class type are direct-initialized (8.5), or copy-initialized from an
expression of the same or a derived class type (8.5), overload resolution selects the
constructor. For direct-initialization, the candidate functions are all the constructors
of the class of the object being initialized. For copy-initialization, the candidate
functions are all the converting constructors (12.3.1) of that class. The argument
list is the expression-list within the parentheses of the initializer.
For a simple test case, see Eli Bendersky's post, here: http://eli.thegreenplace.net/2003/07/23/variable-initialization-in-c/
Always remember the rule:
Whenever, an object is being created and given some value in the same single statement then it is never an assignment.
To add Further,
Case 1:
Vector v1;
Vector v(v1);
Case 2:
Vector v = getVector();
In the above two formats Case 1 is Direct Initialization while Case 2 is known as Copy Initialization.
How does Copy Initialization work?
Copy initialization constructs an implicit conversion sequence: It tries to convert return value of getVector() to an object of type Vector. It can then copy the created object into the object being initialized, So it needs a accessible copy constructor.
The copy constructor actually gets elided in this case (check this) and just the default constructor ends up getting called
EDIT:
The constructor is only sometimes elided, as per Benjamin's answer. For some reason I read that as you calling the constructor directly.

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

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.