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.
Related
This question already has an answer here:
Should an explicit copy constructor be ignored?
(1 answer)
Closed 8 months ago.
I am learning about explicit keyword in C++ using the resources listed here and as this post says:
Prefixing the explicit keyword to the constructor prevents the compiler from using that constructor for implicit conversions.
Now I wrote the following example that uses the explicit copy constructor B::B(const B&) in an implicit conversion. The example is as follows: MRE with C++14 and -fno-elide-constructors flag
struct A {
A()
{
std::cout<<"A's default ctor called"<<std::endl;
}
A(const A&)
{
std::cout<<"A's copy ctor called"<<std::endl;
}
};
class B
{
public:
B()
{
std::cout<<"B default ctor called"<<std::endl;
}
B(A)
{
std::cout<<"B's parametrized ctor called"<<std::endl;
}
explicit B(const B&)
{
std::cout<<"explicit B copy ctor called"<<std::endl;
}
};
int main()
{
B x;
//B a = x; //this fails AS EXPECTED
A y;
B p = y; //Why doesn't this fail as well? This uses the copy ctor in addition to the param ctor. How the explicit copy ctor is used here in implicit conversion
}
The output of the above program is:
B default ctor called
A's default ctor called
A's copy ctor called
B's parametrized ctor called
explicit B copy ctor called <--------------HOW IS THAT EXPLICIT CTOR IS USED HERE IN IMPLICIT CONVERSION?
So my question is that how is it possible to use an explicit copy ctor in the above implicit conversion. I mean to my current understanding I expected B p = y; to fail just like the copy initialization B a = x; fails. I want to know is this allowed by the standard or is it a compiler bug.
Note
Note the use of -fno-elide-constructors flag in my given demo.
I want to know is this allowed by the standard or is it a compiler bug.
This is well-formed and is allowed by the standard as explained below.
The behavior of your program can be understood using dcl.init#17.6.2 which states:
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.
(emphasis mine)
Let's apply this to your example.
In the given example(B p = y; in particular) the source type is A while the destination type is B. Now there is a user-defined conversion available from the source type to the destination type using the non-explicit parameterized ctor B::B(A) which will be used here.
This non-explicit ctor will be called with the initializer expression y as its argument. Moreover, this call is a prvalue of type B.
The important thing to note here is that this call will be used to direct-initialize the object named p. And since in direct initialization context the explicit ctor can be used, therefore the explicit copy ctor can and will be used here.
The effect is as-if we're writing:
B p{/*prvalue of type B obtained from call to parameterized ctor*/};
I could not find where in the standard it is stated that it is forbidden to explicitly default a copy-constructor and copy-assignment with a volatile& or const volatile& argument, like this:
struct A{
A(const volatile A&) =default; // fails to compile on (all) compilers
};
In [dcl.fct.def.default] there is no such a restriction, while [class.copy] specifies that A(const volatile A&) is a copy constructor.
Note: I am just looking for the location in the text of the standard which specifies this behavior.
You are in the right sections, but are overlooking some crucial bullets.
[dcl.fct.def.default]/1:
A function definition of the form:
...
is called an explicitly-defaulted definition. A function that is
explicitly defaulted shall
have the same declared function type (except for possibly differing ref-qualifiers and except that in the case of a copy
constructor or copy assignment operator, the parameter type may be
“reference to non-const T”, where T is the name of the member
function's class) as if it had been implicitly declared, and
[class.copy.ctor]/7:
The implicitly-declared copy constructor for a class X will have the
form
X::X(const X&)
if each potentially constructed subobject of a class type M (or array
thereof) 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&)
...
119) This implies that the reference parameter of the implicitly-declared copy constructor cannot bind to a volatile lvalue;
When the above is summed up, your only two options for explicitly defaulting a copy c'tor are these:
struct A {
A(const A&) = default;
};
struct B {
B(B&) = default;
};
When the standard says A(const volatile A&) is a copy constructor. It means that a user-provided c'tor with such a parameter can be the classes copy c'tor.
Consider the following program and the comments in it:
template<class T>
struct S_ {
S_() = default;
// The template version does not forbid the compiler
// to generate the move constructor implicitly
template<class U> S_(const S_<U>&) = delete;
// If I make the "real" copy constructor
// user-defined (by deleting it), then the move
// constructor is NOT implicitly generated
// S_(const S_&) = delete;
};
using S = S_<int>;
int main() {
S s;
S x{static_cast<S&&>(s)};
}
The question is: why does not user-defining the template constructor (which effectively acts as a copy constructor when U = T) prevent the compiler from generating the move constructor, while, on the contrary, if I user define the "real" copy constructor (by deleting it), then the move constructor is not generated implicitly (the program would not compile)? (Probably the reason is that "template version" does not respect the standard definition of copy-constructor also when T = U?).
The good thing is that that seems to apparently be what I want. In facts, I need all the copy and move constructors and all the move and copy assignment operators that the compiler would generate implicitly as if S was simply defined as template<class U> S{}; plus the template constructor for the conversions from other S<U>. By standard, can I rely on the above definition of S to have all the mentioned stuff I need? If yes, I could then avoid to "default'ing" them explicitly.
The answer is simple - There is no (!) template copy constructor. Even if the template parameter matches the parameter of a copy constructor it is no copy constructor.
See 12.8 Copying and moving class objects
A non-template constructor for class X is a copy constructor if its
first parameter is of type X&, const X&, volatile X& or const volatile
X&, and either there are no other parameters or else all other
parameters have default arguments (8.3.6). [ Example: X::X(const X&)
and X::X(X&,int=1) are copy constructors.
Similar applies to the move constructor
There is no such thing as a templated copy constructor. The regular (non-templated) copy constructor is still generated and a better match than the templated one with the signature you provided (things get more complicated with "universal" references thrown in the mix, but that is off-topic).
Copy constructors, and move constructors, are not templated. The C++ standard is specific on what constitutes a copy constructor.
From: http://en.cppreference.com/w/cpp/language/copy_constructor
A copy constructor of class T is a non-template constructor whose first parameter is T&, const T&, volatile T&, or const volatile T&, and either there are no other parameters, or the rest of the parameters all have default values.
Does a templated constructor (such as the following) override the implicit copy constructor?
template <class T>
struct Foo
{
T data;
// ...
template <class U>
Foo(const Foo<U> &other) : data((T)doSomethingWith(other.data)) {}
// ...
};
If so, does it still override it if other is passed by value rather than constant reference?
If so, is there any way around this without explicitly defining a copy constructor?
No, that is not a copy constructor. Section 12.8 ([class.copy]) of the Standard requires that:
A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&, volatile X& or const volatile X&, and either there are no other parameters or else all other parameters have default arguments.
The compiler will still implicitly generate a defaulted one.
You can make that explicit (requires C++11) by
Foo(const Foo<T>&) = default;
Does a templated constructor (such as the following) override the implicit copy constructor?
No. The copy constructor is still implicitly declared, and is chosen in preference to the template.
Is there any way around this without explicitly defining a copy constructor?
No. If you don't want the implicit copy constructor, then you'll have to define one yourself.
A templated constructor or assignment operator which looks like a templated [default constructor/copy constructor/move constructor/copy assignment operator/move assignment operator] is not really a [default constructor/copy constructor/move constructor/copy assignment operator/move assignment operator] and will not replace it or prevent it from being implicitly generated.
I have a class (Uniform) that has a constructor with 2 parameters, and a default copy constructor (it only contains int, floats, a std::vector and a std::map). I created a
std::vector<Uniform> uniforms
that I want to fill using the
uniforms.push_back()
line. I use this code to do that (the 2nd line is just here to test the copy constructor, as it currently fails)
Uniform uni(uniform_name,type);
Uniform uni2=uni;
uniforms.push_back(uni2);
The default constructor works fine, the "uni2=uni" compiles without problem (so the default copy constructor is OK too), but the push_back returns (using g++ as a compiler):
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.0/../../../../include/c++/4.6.0/ext/new_allocator.h:108:9: erreur: no matching function for call to ‘Uniform::Uniform(const Uniform&)’
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.0/../../../../include/c++/4.6.0/ext/new_allocator.h:108:9: note: candidates are:
./inc/uniform.h:16:5: note: Uniform::Uniform(std::string, Uniform_Type)
./inc/uniform.h:16:5: note: candidate expects 2 arguments, 1 provided
./inc/uniform.h:14:7: note: Uniform::Uniform(Uniform&)
./inc/uniform.h:14:7: note: no known conversion for argument 1 from ‘const Uniform’ to ‘Uniform&’
Thanks :)
When you say "default copy constructor" (which generally makes little sense), I assume you mean "implicitly-declared copy constructor" or "compiler-provided copy constructor"
The exact signature of the compiler-provided copy constructor will depend on the contents of your Uniform class. It could be Uniform::Uniform(const Uniform &) or Uniform::Uniform(Uniform &) depending, again, on the details of Uniform (which you didn't provide).
For example, if your Uniform includes a subobject (base or member) of type T, whose copy constructor is declared as T::T(T &) (no const), then Uniform's implicit constructor will also be implicitly declared as Uniform::Uniform(Uniform &) (no const).
A full specification can be found in the language standard (12.8/5)
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
nonstatic 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&.
Otherwise, the implicitly
declared copy constructor will have
the form
X::X(X&)
An
implicitly-declared copy constructor
is an inline public member of its
class.
The push_back implementation needs Uniform::Uniform(const Uniform &), but something in your class causes it to be Uniform::Uniform(Uniform &). Hence the error. There's no way to say what it is without seeing the definition of your Uniform.
Your copy constructor needs to take its argument as a const reference:
Uniform::Uniform(const Uniform& other)
Your copy constructor should accept const Uniform& and not Uniform& as the one you have does.
You failed to include the copy constructor (sic!!!) but you must have defined it wrongly:
Uniform::Uniform(Uniform&)
{
....
}
should be (note the const)
Uniform::Uniform(const Uniform&)
{
....
}