struct T
{
int a;
};
struct C
{
T& r;
C(T& v) : r(v) {}
};
struct E : T
{
T& r;
E(T const& v) : r(*this), T(v) {} // ok
};
struct F : C, T // base order doesn't matter here
{
//F(T const& v) : C(*this), T(v) {} // error : C::r is not initialized properly
F(T const& v) : C(*static_cast<T*>(this)), T(v) {} // ok
//F(T const& v) : C(static_cast<T&>(*this)), T(v) {} // ok
};
int main()
{
T v;
F f(v);
f.r.a = 1;
}
Although using this pointer in initializer list could be problem, but I've never expected this happened to PODs and may be simply fixed by explicit cast;
Is this some compiler bug or std related problem?
The code is ambiguous.
For constructing the C base of F, the context is direct-initialization, so 13.3.1.3 applies:
c++11
13.3.1.3 Initialization by constructor [over.match.ctor]
For direct-initialization, the candidate
functions are all the constructors of the class of the object being initialized.
The implicitly-declared copy constructor is included, per 12.8:8.
The candidates for the constructor of C are C(T &) and (the default copy constructor) C(const C &), by parameter list (F). In both cases we have a reference binding (13.3.3.1.4) followed by a derived-to-base Conversion (13.3.3.1), with an additional cv-qualification adjustment in the latter case, giving overall rank of Conversion in both cases.
Since C and T are both base classes of F, but are distinct types and neither is a base class of the other, none of the clauses in 13.3.3.2:3 nor 13.3.3.2:4 apply and conversion sequences are indistinguishable.
Indeed, gcc-4.5.1 rejects the code with:
prog.cpp: In constructor 'F::F(const T&)':
prog.cpp:20:34: error: call of overloaded 'C(F&)' is ambiguous
prog.cpp:9:5: note: candidates are: C::C(T&)
prog.cpp:7:1: note: C::C(const C&)
When you try to initialize the base C of F with *this, both the compiler generated copy constructor for C and the constructor that you define taking a T& are a match as the type of *this (F) is derived directly from both C and T. Your cast resolves this ambiguity.
I am surprised that the copy constructor is a better match than the one taking T& as I would have thought that they would both be equally preferred. If the copy-constructor is chosen then the base will be initialized from itself which causes undefined behavior as the reference member will be initialized from an uninitialized reference (itself).
1)never use this in initialization list
http://msdn.microsoft.com/en-us/library/3c594ae3(v=vs.80).aspx
The this pointer is valid only within nonstatic member functions. It cannot be used in the initializer list for a base class.
The base-class constructors and class member constructors are called before this constructor. In effect, you've passed a pointer to an unconstructed object to another constructor. If those other constructors access any members or call member functions on this, the result will be undefined. You should not use the this pointer until all construction has completed.
shortly: C.r initialized by bad poiter
struct C
{
T& r;
C(T& v) : r(v) {}
};
struct E : T
{
T& r;
E(T const& v) : r(*this), T(v) {} // ok
};
You need to initialize any reference at the time of declaration but here you have just declared it. This is not allowed in C++.
Related
struct B {
B(int) {}
B(B const&) {}
};
struct D: B {
using B::B;
};
int main(void) {
B b(5);
D d(b); // error
return 0;
}
c++14 explicitly excludes copy/move constructors from inherited constructors in 12.9 [class.inhctor]/p3.
For each non-template constructor in the candidate set of inherited
constructors other than a constructor having no parameters or a
copy/move constructor having a single parameter, a constructor is
implicitly declared with the same constructor characteristics unless
there is a user-declared constructor with the same signature in the
complete class where the using-declaration appears or the constructor
would be a default, copy, or move constructor for that class.
But I could not find any detailed descriptions in c++17. clang/gcc show that copy/move constructors of base class are not inherited. Can someone provide where it is explained in the standard? Thanks.
The new wording is in [over.match.funcs]/8:
A constructor inherited from class type C ([class.inhctor.init]) that has a first parameter of type “reference to cv1 P” (including such a constructor instantiated from a template) is excluded from the set of candidate functions when constructing an object of type cv2 D if the argument list has exactly one argument and C is reference-related to P and P is reference-related to D. [ Example:
struct A {
A(); // #1
A(A &&); // #2
template<typename T> A(T &&); // #3
};
struct B : A {
using A::A;
B(const B &); // #4
B(B &&) = default; // #5, implicitly deleted
struct X { X(X &&) = delete; } x;
};
extern B b1;
B b2 = static_cast<B&&>(b1); // calls #4: #1 is not viable, #2, #3, and #5 are not candidates
struct C { operator B&&(); };
B b3 = C(); // calls #4
— end example ]
In your example, B's inherited copy constructor is excluded from the set of candidates (that constructor has a first parameter of type reference to const B, the argument list has exactly one argument - b, and B and D are reference-related).
The passage you quote does not in fact prevent inheritance of copy constructors in C++14. Consider this instead:
B(B const&, int = 42) {}
This is a copy constructor, but it has two parameters. The passage only excludes a copy constructor with a single parameter. And when you supply both arguments, you can in fact initialize a D object with this constructor.
g++ error messages provide some insight.
note: an inherited constructor is not a candidate for initialization from an expression of the same or derived type
Aha! A quick search in the draft standard finds this
A constructor inherited from class type C (class.inhctor.init) that has a first parameter of type “reference to cv1 P” (including such a constructor instantiated from a template) is excluded from the set of candidate functions when constructing an object of type cv2 D if the argument list has exactly one argument and C is reference-related to P and P is reference-related to D.
I have this code(stolen from cplusplus.com):
// explicit:
#include <iostream>
using namespace std;
class A {};
class B {
public:
explicit B (const A& x) {}
B& operator= (const A& x) {return *this;}
operator A() {return A();}
};
void fn (B x) {}
int main ()
{
A foo;
B bar (foo);
bar = foo;
foo = bar;
// fn (foo); // not allowed for explicit ctor.
fn (bar);
return 0;
}
So apparently the statement fn (foo); would call the constructor defined in class B, and I do not really get why. Why would that call a constructor with an argument, I mean aren't things simply copied to the function parameters when not using a reference? If fn (foo); calls the constructor B (const A& x) {}, shouldn't fn (bar); generate an error since there is no constructor defined for an argument of type B, such as B (const B& x) {}?
It's all because of fn()'s signature:
void fn (B x) {}
fn only accepts objects of type B, and passes them by value.
So what happens when calling fn() with an object of type A?
A foo;
fn (foo); // not allowed for explicit ctor.
The compiler tries to find the best matching function - a function called fn and receives type A. (Overload resolution)
Because there is no such function, it then tries to find the next best match by casting the object to a type that fn() accepts.
A conversion constructor would usually do the job : B (const A& x) {}
But because it's marked with an explicit specifier the compiler can't do it implicitly.
You would have to explicitly cast the object for the compiler to find a match:
fn ((B)foo); // Explicit conversion is allowed.
That is a result of an explicit creation of an object
fn(foo);
is not implicitly possible since fn() doesn't takes any A object as parameter
BUT your foo parameter can actually be used to create an instance of B which match perfectly the fn parameter, you can prevent that implicitly conversion of objects using the explicit keyword.
And that is what you are doing here:
explicit B (const A& x) {}
From the C++ Standard
15 The initialization that occurs in the = form of a brace-or-equal-initializer or condition (6.4), as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.6.1), is called copy-initialization. [ Note: Copy-nitialization may invoke a move (12.8).—end note ]
Thus for the explicit constructor
explicit B (const A& x) {}
this initialization that in fact is used for the initialization of the function parameter
A foo;
B bar = foo;
is wrong.
It is the direct initialization that can be used with the explicit constructor as it is shown in your program
A foo;
B bar( foo );
Well ... B(const B&) although not explicitly declared, is there. That's because it is one of the special functions for which the compiler has a default:
B()
B(const B&)
B(B&&)
B& operator=(const B&)
B& operator==(B&&)
~B()
So, B can copy itself.
It cannot convert implicitly form A being such a conversion is declared as explicit.
fn(B(foo)) would have worked.
How to initialize function parameters or function return values when the type has no copy constructor and only explicit constructors? For example:
struct A { };
struct B {
B(const B&) = delete;
explicit B(const A&) {}
};
void foo(B) {}
B bar() {
A a;
return ...; // how to return a?
}
int main() {
A a;
foo( ... ); // how to pass a?
}
return a / foo(a) does not work because constructor B(const A&) is explicit. return B(a) / foo(B(a)) does not work because copy constructor is deleted.
My intention is to know the language rules. To me it looks like a flaw in the language that, considering the presented type B, I can initialize locals, statics and class members but apparently neither function parameters nor function return values.
B(const B&) = delete;, without an explicit definition of move-constructor, means the object cannot be copied or moved.
This means that you cannot pass by value, either as a parameter or as a return.
The explicit constructor is a red herring.
If you don't want to change B to enable copying or moving, then you will have to pass by reference.
answers in annotated code below:
struct A { };
struct B {
B(const B&) = delete;
B(B&&) = default; // how to return B - pt 1 : move constructor
explicit B(const A&) {}
};
void foo(B) {}
B bar() {
A a;
return B(a); // how to return B - pt 2 : explcitly convert it
}
int main() {
A a;
foo(B(a)); // how to pass a - explicit conversion
}
I think your issue is a misconception:
You do not initialize function parameters or return values with the constructor.
Instead you hand in parameters to a function, handing in means copying them.
Depending on the function signature either the variable itself is copied (void foo(B b)) or the reference/pointer to it is copied.
In the first case you need a copy constuctor for your type, while you do not need one for references/pointers.
The same is true for returning values (!) from a function.
You also need a copy constructor there.
But you deleted the copy constructor, thus the compile errors.
With C++11 move semantics were added as an optimisation opportunity.
When a move constructor is available r-values will be moved in/out of functions automatically instead of copied.
Returning a local variable will also move it out instead of copying.
Since you declared a copy consturctor (no matter if as deleted/default) no move related functions will be created by the compiler.
And thus also the automatic moving fails.
class A {
public:
A(int v) {
_val = v;
}
private:
int _val;
};
class B {
public:
B(int v) {
a = A(v); // i think this is the key point
}
private:
A a;
};
int main() {
B b(10);
return 0;
}
compiler says:
test.cpp: In constructor ‘B::B(int)’:
test.cpp:15: error: no matching function for call to ‘A::A()’
test.cpp:5: note: candidates are: A::A(int)
test.cpp:3: note: A::A(const A&)
I've learned Java, and I don't know how to deal with this in C++.
Searching for a couple days, plz tell me can C++ do this?
You need to use a Member Initialization List
B(int v):a(v)
{
}
With:
B(int v)
{
a = A(v); // i think this is the key point
}
a is being assigned a value and not being initialized(which is what you intend), Once the body of an constructor begins {, all its members are already constructed.
Why do you get an error?
The compiler constructs a before constructor body { begins, the compiler uses the no argument constructor of A because you didn't tell it otherwiseNote 1, Since the default no argument constructor is not available hence the error.
Why is the default no argument constructor not generated implicitly?
Once you provide any constructor for your class, the implicitly generated no argument constructor is not generated anymore. You provided overloads for constructor of A and hence there is no implicit generation of the no argument constructor.
Note 1
Using Member Initializer List is the way to tell the compiler to use particular overloaded version of the constructor and not the default no argument constructor.
You have to use initialization lists:
class B {
public:
B(int v) : a(v) { // here
}
private:
A a;
};
Otherwise the compiler will try to construct an A using the default constructor. Since you don't provide one, you get an error.
Yes it can, but you haven't provided a default constructor for A(one that takes no parameters or all parameters have default values), so you can only initialize it in the initializer list:
B(int v) : a(v)
{
}
This is because before the constructor body enters, a will be constructed (or attempted to be constructed) with the default constructor (which isn't available).
I always think I know C++ pretty well, but sometimes I'm surprised by even the most fundamental things.
In the following scenario, I'm confused as to why the constructor Derived::Derived(const Base&) is invoked:
class Base
{ };
class Derived : public Base
{
public:
Derived() { }
Derived(const Base& b)
{
std::cout << "Called Derived::Derived(const Base& b)" << std::endl;
}
};
int main()
{
Derived d;
Base b;
d = b;
}
This outputs: Called Derived::Derived(const Base& b), indicating that the second constructor in Derived was invoked. Now, I thought I knew C++ pretty well, but I can't figure out why that constructor would be invoked. I understand the whole "rule of four" concept, and I would think that the expression d = b would do one of two things: Either it would 1) invoke the implicit (compiler-generated) assignment operator of Base, or 2) Trigger a compiler error complaining that the function Derived& operator = (const Base&) does not exist.
Instead, it called a constructor, even though the expression d = b is an assignment expression.
So why does this happen?
d = b can happen because b is converted to Derived.
The second constructor is used for automatic type conversion.
It's like d = (Derived) b
Derived isa Base, but Base isn'ta Derived, so it has to be converted before assignment.
assigning base to derived? perhaps you meant (a) by ref (b) or derived to base. This doesn't really make sense, but the compiler is correctly using your (non-explicit) constructor to convert the Base instance to a new Derived instance (which is subsequently assigned into d).
Use an explicut constructor to prevent this from happening automatically.
Personally I think you messed up your code sample, because, normally assigning firstclass base to derived makes no sense without a conversion
There are two interacting features at play here:
Assignment Operators are never inherited
A constructor that is not explicit, or a conversion operator (operator T()) define a user-conversion that can be used implicitly as part of a conversion sequence
Assignement Operators are never inherited
A simple code example:
struct Base {}; // implicitly declares operator=(Base const&);
struct Derived: Base {}; // implicitly declares operator=(Derived const&);
int main() {
Derived d;
Base b;
d = b; // fails
}
From ideone:
prog.cpp: In function ‘int main()’:
prog.cpp:7: error: no match for ‘operator=’ in ‘d = b’
prog.cpp:2: note: candidates are: Derived& Derived::operator=(const Derived&)
Conversion sequence
Whenever there is an "impedance" mismatch, such as here:
Derived::operator= expects a Derived const& argument
a Base& is provided
the compiler will try to establish a conversion sequence to bridge the gap. Such a conversion sequence may contain at most one user-defined conversion.
Here, it will look for:
any constructor of Derived that can be invoked with a Base& (not explicit)
a conversion operator in Base that would yield a Derived item
There is no Base::operator Derived() but there is a Derived::Derived(Base const&) constructor.
Therefore our conversion sequence is defined for us:
Base&
Base const& (trivial)
Derived (using Derived::Derived(Base const&))
Derived const& (temporary object bound to a const reference)
And then Derived::operator(Derived const&) is called.
In action
If we augment the code with some more traces, we can see it in action.
#include <iostream>
struct Base {}; // implicitly declares Base& operator(Base const&);
struct Derived: Base {
Derived() {}
Derived(Base const&) { std::cout << "Derived::Derived(Base const&)\n"; }
Derived& operator=(Derived const&) {
std::cout << "Derived::operator=(Derived const&)\n";
return *this;
}
};
int main() {
Derived d;
Base b;
d = b;
}
Which outputs:
Derived::Derived(Base const&)
Derived::operator=(Derived const&)
Note: Preventing this ?
It is possible, in C++, to remove a constructor for being used in conversion sequences. To do so, one need to prefix the declaration of the constructor using the explicit keyword.
In C++0x, it becomes possible to use this keyword on conversion operators (operator T()) as well.
If here we use explicit before Derived::Derived(Base const&) then the code becomes ill-formed and should be rejected by the compiler.
Since you've defined a constructor for Derived which takes type Base and you are down-casting Base, the compiler chooses the most suitable constructor for the upcast, which in this case is the Dervied(const Base& b) you've defined. If you did not define this constructor you would actually get a compiling error when trying to make the assignment. For more info, you can read the following at Linuxtopia.
It can't assign value of different type, so it should first construct a Derived temporary.