Since I have doubts about this question (for C++03) I am posting it here.I just read about conversion constructors and it states that
"To be a converting constructor, constructor must have single
argument and be declared without keyword explicit."
Now my question is whether the copy constructor can be called a conversion constructor provided it is not explicitly declared ? Does it qualify to be one ?
I believe it cant be called a conversion constructor because it only accepts the same type parameter ths resulting in no conversion. For Instance
foo a;
foo b;
a = 100; //a Conversion constructor would be called (i.e) foo(int a){...}
a = b ; //Since both objects are same type and have been initialized the assignment operator will be called (if there is an overloaded version otherwise the default will be called)
Is my understanding correct ?
Quoting the Standard:
[class.conv.ctor]/3
A non-explicit copy-constructor (12.8) is a converting constructor. An implicitly-declared copy constructor is not an explicit constructor; it may be called for implicit type conversions.
So yes, a copy-ctor is a converting ctor.
Also note [conv]/1 which specifies and points out in a remark:
Note: a standard conversion sequence can be empty, i.e., it can consist of no conversions.
and in /3:
An expression e can be implicitly converted to a type T if and only if the declaration T t=e; is well-formed
So the set of implicit conversions contain the empty conversions.
Yes, a copy constructor is what it is -- a copy constructor. Which isn't a conversion constructor that converts from one type to a different type.
Related
I wonder which compiler is compliant with the standard, i use the following code
#include <iostream>
#include <string>
#include <memory>
#include <vector>
class AbstractBase
{
public:
virtual ~AbstractBase() {};
virtual std::string get_name() = 0;
virtual int get_number() = 0;
};
class BaseImpl : public AbstractBase
{
public:
BaseImpl() = delete; //(a)
//BaseImpl(BaseImpl&) = delete; //(b)
BaseImpl(const std::vector<std::string>& name_) : name(name_) {}
std::string get_name() override {return name.empty() ? std::string("empty") : name.front();}
private:
std::vector<std::string> name{};
};
class impl : public BaseImpl
{
public:
impl() : BaseImpl({}) {}
int get_number() override {return 42;}
};
int main()
{
std::unique_ptr<AbstractBase> intance = std::make_unique<impl>();
std::cout << intance->get_name() << " " << intance->get_number() << "\n";
return 0;
}
msvc and clang produce a compiler error while gcc is fine with this code. Link to godbold
https://godbolt.org/z/ETYGn5T1h
If one explicit delete the copy ctor of BaseImpl (uncomment (b)) all three compiler are fine. Also if one do not explicitly delete (a) the standard ctor (then no standard ctor is generated because there is an user defined ctor), all compilers are fine.
Apparently clang and msvc believe that by : BaseImpl({}) they can either call the user defined ctor, or indirectly via the default ctor the implicit copy ctor. However, since the default ctor is deleted, this ambiguity should not exist at all. However, by explicitly deleting the default ctor, the compilers seem to first assume that a default ctor exists for BaseImpl and generate an error before checking whether it is deleted.
I wonder now if this behavior is standard compliant.
Edit: I'm attaching the compiler output for the case that one don't want to or can't click on the link to the compiler explorer:
Clang and MSVC are correct, and gcc has a bug.
Defining a function as deleted is not the same as not declaring the function.
Except for move constructors, move assignment functions, and some cases of inherited constructors (see [over.match.funcs]/8), a deleted function is considered to exist for purposes of overload resolution. Nothing else in section [over] treats a deleted function specially. And we have [over.best.ics]/2, emphasis mine:
Implicit conversion sequences are concerned only with the type, cv-qualification, and value category of the argument and how these are converted to match the corresponding properties of the parameter. [ Note: Other properties, such as the lifetime, storage class, alignment, accessibility of the argument, whether the argument is a bit-field, and whether a function is deleted, are ignored. So, although an implicit conversion sequence can be defined for a given argument-parameter pair, the conversion from the argument to the parameter might still be ill-formed in the final analysis. — end note ]
So within impl() : BaseImpl({}) {}, the BaseImpl initializer uses overload resolution to select the BaseImpl constructor used to initialize the base class subobject. The candidates are all the constructors of BaseImpl: the intended BaseImpl(const std::vector<std::string>&), the deleted BaseImpl(), the implicitly declared copy constructor BaseImpl(const BaseImpl&), and the implicitly declared (and not deleted) move constructor BaseImpl(BaseImpl&&). At this point, BaseImpl() is not viable since the initializer has one argument. The vector constructor is viable since there is a constructor vector(std::initializer_list<std::string>) which is not explicit and can convert the {} argument to the vector type. The copy and move constructors are also viable since the BaseImpl() constructor is declared, and is not explicit, and "can" convert the {} argument to type BaseImpl. So overload resolution is ambiguous, even though some of the implicit conversion sequences use a deleted function.
When the BaseImpl() = delete; declaration is not present, BaseImpl simply doesn't have any default constructor, since the BaseImpl(const std::vector<std::string>&) declaration prevents implicit declaration of a default constructor. So there is no implicit conversion sequence for {} to BaseImpl, and the copy and move constructors of BaseImpl are not viable for the initialization BaseImpl({}). The vector constructor is the only viable function.
When you declare BaseImpl(BaseImpl&), deleted or not, this is considered a copy constructor (despite missing the usual const), so it prevents the implicit declarations of the copy constructor and move constructor. But this copy constructor is not viable for BaseImpl({}), since the reference to non-const type can't bind to the rvalue temporary BaseImpl object involved in using BaseImpl() (see [over.ics.ref]/3). So only the intended vector constructor is viable.
WIP marker
A comment by #aschepler has thrown the conclusion in doubt. See there for details or to pitch in if you're a language lawyer. I'll have a closer look tonight, then update this answer.
TL;DR
GCC is correct in a quite non-obvious way.
What is happening
The clang error message seems pretty clear:
<source>:28:14: error: call to constructor of 'BaseImpl' is ambiguous
impl() : BaseImpl({}) {}
^ ~~
<source>:14:7: note: candidate constructor (the implicit move constructor)
class BaseImpl : public AbstractBase
^
<source>:14:7: note: candidate constructor (the implicit copy constructor)
<source>:19:5: note: candidate constructor
BaseImpl(const std::vector<std::string>& name_) : name(name_) {}
^
1 error generated.
Compiler returned: 1
There are three constructors that are available for the BaseImpl({}) call -- the generated copy and move constructors, and the vector constructor. Clang does not know which of these to choose.
MSVCs error is a bit less straightforward:
x64 msvc v19.latest (Editor #1)
x64 msvc v19.latest
x64 msvc v19.latest
/std:c++20
123
<Compilation failed>
# For more information see the output window
x64 msvc v19.latest - 2681ms
Output of x64 msvc v19.latest (Compiler #1)
example.cpp
<source>(28): error C2259: 'BaseImpl': cannot instantiate abstract class
<source>(14): note: see declaration of 'BaseImpl'
<source>(28): note: due to following members:
<source>(28): note: 'int AbstractBase::get_number(void)': is abstract
<source>(11): note: see declaration of 'AbstractBase::get_number'
Compiler returned: 2
What happens here is that MSVC attempts to call the copy or move constructor, that for this it attempts to create a temporary of type BaseImpl from the initializer {}, and that this fails because BaseImpl is abstract before it fails because the default constructor is deleted (so you don't get an error message about that).
GCC does not consider the copy and move ctors even if they are explicitly added and so just constructs a vector and compiles fine.
What should happen
Let's dive into the standard. In particular, let's have a look at [dcl.init.general]. I'll omit non-matching parts of the standard language and denote that with (...).
First note that according to [dcl.init.general] (15)
15 The initialization that occurs
(15.1) — for an initializer that is a parenthesized expression-list or a braced-init-list,
(...)
is called direct-initialization.
There follows in [dcl.init.general] (16) a long list of conditions for what happens in initialization. The relevant here is (16.6)
(16.6) — Otherwise, if the destination type is a (possibly cv-qualified) class type:
(...)
(16.6.2) — Otherwise, 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. The applicable constructors are enumerated (12.4.2.4), and the best one is chosen through overload resolution (12.4). Then:
(16.6.2.1) — If overload resolution is successful, the selected constructor is called to initialize the object, with the initializer expression or expression-list as its argument(s).
(...)
(16.6.2.3) — Otherwise, the initialization is ill-formed.
What this boils down to is: we look for all applicable constructors and attempt to choose a correct one. If that works, we use it, otherwise it's an error.
So let's take a look at overload resolution in [over.match.ctor]. Here it states that
1 When objects of class type are direct-initialized (9.4), (...), overload resolution selects the constructor. For direct-initialization or default-initialization that is not in the context of copy-initialization, the candidate functions are all the constructors of the class of the object being initialized. (...). The argument list is the expression-list or assignment-expression of the initializer.
So our set of candidate functions are the generated copy and move ctors as well as the vector ctor. Next step is checking which of these are viable according to [over.match.viable]. This means first checking that the number of arguments in the call fits the candidate functions (true for all candidates) and then that
4 Third, for F to be a viable function, there shall exist for each argument an implicit conversion sequence (12.4.4.2) that converts that argument to the corresponding parameter of F. If the parameter has reference type, the implicit conversion sequence includes the operation of binding the reference, and the fact that an lvalue reference to non-const cannot be bound to an rvalue and that an rvalue reference cannot be bound to an lvalue can affect the viability of the function (see 12.4.4.2.5).
An implicit conversion sequence is, according to [over.best.ics.general],
3 A well-formed implicit conversion sequence is one of the following forms: >
(3.1) — a standard conversion sequence (12.4.4.2.2),
(3.2) — a user-defined conversion sequence (12.4.4.2.3), or
(3.3) — an ellipsis conversion sequence (12.4.4.2.4).
where a standard conversion sequence is chiefly concerned with stuff like int to long, lvalue to rvalue, ref to const ref etc. We're interested in user-defined conversion sequences here, which are
1 A user-defined conversion sequence consists of an initial standard conversion sequence followed by a user- defined conversion (11.4.8) followed by a second standard conversion sequence. If the user-defined conversion is specified by a constructor (11.4.8.2), the initial standard conversion sequence converts the source type to the type of the first parameter of that constructor. (...)
2 The second standard conversion sequence converts the result of the user-defined conversion to the target type for the sequence; any reference binding is included in the second standard conversion sequence. (...).
(...)
There is quite definitely a user-defined conversion sequence from {} to std::vector<std::string>. Because BaseImpl's default constructor is deleted, there is not a user-defined conversion sequence from {} to BaseImpl; this would require two user-defined conversions: one to std::vector<std::string> and another to BaseImpl.
So of the three candidate constructors, only the std::vector<std::string> constructor is viable and eligible for overload resolution and should be chosen. GCC does this, and unless I made an error in my analysis, MSVC and clang have a bug.
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.
class AAA {
public:
explicit AAA(const AAA&) {}
AAA(int) {}
};
int main() {
AAA a = 1;
return 0;
}
In the above code, as I understand, though elided in most cases, the copy constructor is still semantically required to be called. My question is, is the call explicit or implicit? For a long time I have the conclusion in my mind that the call to AAA::AAA(int) is implicit but the call to the copy constructor is not. Today I accidentally got g++ to compile the above code and it reported error. (VC12 compiles OK.)
In section 8.5 of the standard:
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. The applicable constructors
are enumerated (13.3.1.3), and the best one is chosen through overload
resolution (13.3). The constructor so selected is called to initialize
the object, with the initializer expression or expression-list as its
argument(s). If no constructor applies, or the overload resolution is
ambiguous, the initialization is ill-formed.
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). If the
conversion cannot be done or is ambiguous, the initialization is
ill-formed. 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.
The bolded direct-initialize in the above quotes means the call to copy constructor is explicit, right? Is g++ wrong or my interpretation of the standard wrong?
Looks like this bug: g++ fails to call explicit constructors in the second step of copy initialization
g++ fails to compile the following code
struct X
{
X(int) {}
explicit X(X const &) {}
};
int main()
{
X x = 1; // error: no matching function for call to 'X::X(X)'
}
The second step of a copy initialization (see 8.5/16/6/2) is a
direct-initialization where explicit constructors shall be considered
as candidate functions.
Looks like copy constructor is never called . Only constructor is called . The following code may call copy constructor
AAA a = 1;
AAA ab = a;
Not sure why G++ is compiling it .
Class X -> converted to Y by two ways 1) constructors, and 2) by conversion functions.
I understood the single argument constructor is used for conversion.
In the specification:
An implicitly-declared copy constructor is not an explicit constructor; it may be called for implicit type conversion.
Question:
So, that means not only single argument constructor is used for the conversion, but also copy constructor?. If so, which scenario it is used?. any snippet of sample code?
Kindly bear with me if the question is very basis.
Copy constructor is not an explicit constructor, so it will be used wherever possible. Copy constructor will "convert" only from the same type, so it is not a conversion in the full sense. However, for the sake of generality it is handy to call it one.
Read this paper: http://www.keithschwarz.com/cs106l/winter20072008/handouts/180_Conversion_Constructors.pdf if you want more details on conversion constructors.
It basically means that you can do:
struct A {};
A a;
A b = a;
If the copy constructor was marked explicit that would fail to compile. You can test it by adding: explicit A( A const & ) {} to the struct and recompiling the program.
Implicitly-declared copy constructor cannot use for conversions, since it's copy-ctor, that has declared as T(const T&) or T(T&).
draft n3337 par 12.8 C++ standard.
8 The implicitly-declared copy constructor for a class X will have the
form X::X(const X&) if — each direct or virtual base class B of X has
a copy constructor whose first parameter is of type const B& or const
volatile B&, and — for all the non-static data members of X that are
of a class type M (or array thereof), each such class type has a copy
constructor whose first parameter is of type const M& or const
volatile M&.119 Otherwise, the implicitly-declared copy constructor
will have the form X::X(X&)
Since copy c-tor is not explicit you can use such code
struct So
{
};
int main()
{
So s = So();
}
If copy-ctor is explicit you could use only initizaliation like So s((So()));
An implicit copy constructor is one that the compiler writes for you. It always has the form
T(const T&);
This means that any object which has a conversion operator to const T& can be implicitly copied, even if this isn't what you wanted. The most common way to trigger this is to make a copy to a base class from a derived class; this is called object slicing because the copy won't have the same type as the original and will probably lose some important properties or behavior.
The C++ Standard Library by Nicolai M. Josuttis states:
There is a minor difference between
X x;
Y y(x) //explicit conversion
and
X x;
Y y = x; //implicit conversion
Following to say: "The former creates a new object of type Y by using an explicit conversion from type X, whereas the latter creates a new object of type Y by using an implicit conversion."
I'm a little confused about the concepts of explicit vs implicit conversion I guess. In both cases you're taking an X and pushing it into a Y per se - one uses a Y's constructor and one uses the assignment operator though.
What's the difference in how the conversion is treated in these two cases, what makes it explicit/implicit, and how does this tie into making a class constructor defined with the "explicit" key word, if at all?
one uses a Y's constructor and one uses the assignment operator though.
Nope. In the second case it's not an assignment, it's an initialization, the assignment operator (operator=) is never called; instead, a non-explicit one-parameter constructor (that accepts the type X as a parameter) is called.
The difference between initialization and assignment is important: in the first case, a new object is being created, and it starts its life with the value that it is being initialized with (hence why a constructor is called), while assignment happens when an object is assigned (~copied) to an object that already exists and already is in a definite state.
Anyway, the two forms of initialization that you wrote differ in the fact that in the first case you are explicitly calling a constructor, and thus any constructor is acceptable; in the second case, you're calling a constructor implicitly, since you're not using the "classical" constructor syntax, but the initialization syntax.
In this case, only one-parameter constructors not marked with explicit are acceptable. Such constructors are called by some people "converting" constructors, because they are involved in implicit conversions.
As specified in this other answer, any constructor not marked as explicit can take part in an implicit conversion for e.g. converting an object passed to a function to the type expected by such function. Actually, you may say that it's what happens in your second example: you want to initialize (=create with a value copied from elsewhere) y with x, but x first has to be converted to type Y, which is done with the implicit constructor.
This kind of implicit conversion is often desirable: think for example to a string class that has a converting (i.e. non-explicit) constructor from a const char *: any function that receives a string parameter can also be called with a "normal" C-string: because of the converting constructor the caller will use C-strings, the callee will receive its string object.
Still, in some cases one-parameters constructors may not be appropriate for conversion: usually this happens when their only parameter is not conceptually "converted" to the type of the object being created, but it is just a parameter for the construction; think for example about a file stream object: probably it will have a constructor that accepts the name of the file to open, but it makes no sense to say that such string is "converted" to a stream that works on that file.
You can also find some more complex scenarios where these implicit conversions can completely mess-up the behavior that the programmer expects from overload resolution; examples of this can be found in the answers below the one I linked above.
More simply, it can also happen that some constructors may be very heavyweight, so the class designer may want to make sure that they are invoked explicitly. In these cases, the constructor is marked as explicit, so it can be used only when called "explicitly as a constructor" and doesn't take part in implicit conversions.
one uses the assignment operator though
No, it does not. It calls the constructor directly.
The reason why one is explicit and one is implicit is because implicit conversions can happen when you don't want them to. Explicit ones cannot. The easiest example of this is bool.
Let's say that you invent some type which can be either true or false- like a pointer. Then let's further say that you decide that in order to make life easier for your users, you let it convert to bool implicitly. This is great- right up until the point where one of your users does something dumb.
int i = 0;
i = i >> MyUDT();
Oh wait- why does that even compile? You can't shift a MyUDT at all! It compiles because bool is an integral type. The compiler implicitly converted it to a bool, and then to something that can be shifted. The above code is blatantly dumb- we only want people to be able to convert to a bool, not a bool and anything else that a bool might want to do.
This is why explicit conversion operators were added to C++0x.
The first form is direct initialization. The second is copy initialization.
Copy initialization implicitly calls a converting constructor or conversion operator and then explicitly calls a copy constructor (the copy constructor call may be elided, but an accessibility check must still be performed).
Consider a third possibility, which is copy-initialization, but the conversion is explicit:
Y y = Y(x);
or
Y y = (Y)x;
The Implicit casting doesn't require any casting operator. This casting is normally used when converting data from smaller integral types to larger or derived types to the base type.
int iVal = 100;
double dVal = iVal;
The explicit converting constructor is preferred to the implicit conversion operator because in the latter case there is an additional call to the copy constructor.Implicit and Explicit converstions