Im' trying to initialize my "cl" class with that :
class Base
{
Base(int x){}
private :
Base(){}
};
class Test
{
public:
Base cl(12);
};
But I get (with gcc) error: expected identifier before numeric constant
And I don't want to set the "Base" default constructor public
Any "workaround" ?
This is parsed as a function declaration, resulting in an error because the parameter should be a type:
Base cl(12);
You probably want a Base data member, initialized with the value 12:
Base cl{12}; // requires C++11
If you don't have C++11 support, then you can initialize cl in Test's constructor initialization list:
class Test
{
public:
Test() : cl(12) {}
Base cl;
};
Data-members cannot be initialized directly in class through a constructor call. Because of the Most Vexing Parse1, your c1 member is parsed as a function declaration. An incorrect declaration, albeit.
In C++03 and before, you would need to initialize your class through the initializer-list of the constructor:
class Test
{
public:
Test() : cl(21)
// ^^^^^^^^
{};
Base cl;
};
In C++11, this can be easily done through uniform-initialization:
class Test
{
public:
Base cl{21};
// ^^^^^^^
};
Footnote:
1: The most vexing parse is a specific form of syntactic ambiguity resolution in the C++ programming language.... ~ Wikipedia
Related
I am using Visual Studio 2019 (v16.10.3) with /std:c++latest and this compiles:
class Base
{
public:
Base(int x) {}
};
class Derived : public Base
{
// no constructors declared in Derived
};
int main() {
Derived d(5);
}
For the previous versions of the standard I have to declare inherited constructors with the using directive:
class Derived : public Base
{
using Base::Base;
};
Is this something new that was put in C++20 or is it some Microsoft specific thing?
Is this something new that was put in C++20 or is it some Microsoft specific thing?
Not with relation to inherited constructors. What changed is that aggregate initialization may use parenthesis under certain conditions. Derived is considered an aggregate on account of having no private parts, so we initialize its bases and members directly.
It even works when we add a public member:
class Base
{
public:
Base(int ) {}
};
struct Derived : public Base
{
// no constructors declared in Derived
int y;
};
int main() {
Derived d(5, 4);
}
Live
This is the result of C++17 allowing classes with base classes to still be considered aggregates combined with C++20 rules allowing aggregate initialization to work with () constructor syntax if the values in the parentheses would work (and wouldn't call a constructor). So while it looks like a constructor call, it's actually aggregate initialization.
Derived doesn't have a constructor matching Base's constructor; you're merely initializing the aggregate Derived by providing a valid initializer for its subobject Base.
I have the following code snippet and expect that constructor from parent class First::Inner will be called.
class First {
public:
class Inner {
public:
Inner(int x) {}
virtual ~Inner() = default;
};
virtual Inner* begin() = 0;
};
class Second: public First {
public:
class Inner: public First::Inner {
};
Inner* begin() {
return new Inner(1);
}
};
int main()
{
Second s;
return 0;
}
Instead I got a compile error in the compiler:
main.cpp: In member function ‘virtual Second::Inner* Second::begin()’:
main.cpp:16:31: error: no matching function for call to ‘Second::Inner::Inner(int)’
It works if moved the entire constructor Inner(int x) {} from base class First::Inner to derived Second::Inner. But I'd like to keep the constructor in the base class.
What's wrong with the code and how could I fix the error?
The problem is that the class Second::Inner doesn't have a constructor taking int, then new Inner(1); would fail.
You can inherit constructor like
class Inner: public First::Inner {
using First::Inner::Inner;
};
LIVE
If the using-declaration refers to a constructor of a direct base of
the class being defined (e.g. using Base::Base;), all constructors of
that base (ignoring member access) are made visible to overload
resolution when initializing the derived class.
If overload resolution selects one of the inherited constructors when
initializing an object of such derived class, then the Base subobject
from which the constructor was inherited is initialized using the
inherited constructor, and all other bases and members of Derived are
initialized as if by the defaulted default constructor (default member
initializers are used if provided, otherwise default initialization
takes place). The entire initialization is treated as a single
function call: initialization of the parameters of the inherited
constructor is sequenced-before initialization of any base or member
of the derived object.
In an attempt to answer another question, I came up with a scheme to force children of a CRTP base class to accept a particular type as a parameter in their constructors: make the parameter type's constructor private, assign the CRTP base class as a friend, and declare the parameter type as a parameter for the base class constructor as well.
However, when I tried to demonstrate that this scheme provided the desired protections via access violations, I found that even though the parameter type's constructor was private, the child class was able to construct it:
template <typename T>
class SingletonBase {
protected: class P { friend class SingletonBase<T>; P() = default; };
public:
SingletonBase(P) {}
};
class Logger: public SingletonBase<Logger> {
using BASE = SingletonBase<Logger>;
public:
Logger() : BASE{P{}} {} // WHY NO ACCESS VIOLATION?
};
This compiles without error, even though I'd expect an access violation. Why?
Does “friending” the base class in CRTP inheritance affect the child class as well?
No, of course not. Friendship is not inherited. To illustrate the issue,
Firstly, P::P() is a defaulted default constructor, it's a trivial default constructor.
Secondly, P{} is value initialization (since C++11),
(emphasis mine)
2) if T is a class type with a default constructor that is neither user-provided nor deleted (that is, it may be a class with an implicitly-defined or defaulted default constructor), the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor;
Note it'll be only zero initialized here, not default initializated. The private default constructor of P won't be invoked at all.
If T is an non-union class type, all base classes and non-static data members are zero-initialized, and all padding is initialized to zero bits. The constructors, if any, are ignored.
If you change it to default initialization explicitly, you'll get the access violation error.
Logger() : BASE{P()} {} // error: calling a private constructor of class 'SingletonBase<Logger>::P
// ~~
A simplified demonstration
class X { X() = default; };
int main()
{
X x1{}; // fine
X x2; // error: calling a private constructor of class 'X'
}
LIVE
Solution
You can provide a user-defined default constructor, which is a non-trivial constructor, to change the behavior of value-initialization.
template <typename T>
class SingletonBase {
protected:
class P {
friend class SingletonBase<T>;
P() {} // user-defined default constructor
};
public:
SingletonBase(P) {}
};
class Logger: public SingletonBase<Logger> {
using BASE = SingletonBase<Logger>;
public:
Logger() : BASE{P{}} {} // error: calling a private constructor of class 'SingletonBase<Logger>::P'
};
What you have done has nothing to do with your friend statement!
If you remove your friend the code compiles also fine!
That is because a default constructor for an empty class is public:
From C++11 standard:
If there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted. An implicitly-declared default constructor is an inline public member of its class.
If you have no default constructor like this:
template <typename T>
class SingletonBase
{
protected:
class P
{
friend class SingletonBase<T>;
P(int){ }
};
public:
SingletonBase(P) {}
};
class Logger: public SingletonBase<Logger>
{
using BASE = SingletonBase<Logger>;
public:
Logger() : BASE(P{1}) {} // WHY NO ACCESS VIOLATION?
};
You will get the "access" violation and you see that your friend did not work!:
main.cpp: In constructor 'Logger::Logger()':
main.cpp:10:17: error: 'SingletonBase<T>::P::P(int) [with T = Logger]' is private
P(int){ }
^
main.cpp:22:28: error: within this context
Logger() : BASE(P{1}) {} // WHY NO ACCESS VIOLATION?
Consider following code:
class TBase {
public:
TBase();
TBase(const TBase &);
};
class TDerived: public TBase {
public:
using TBase::TBase;
};
void f() {
TBase Base;
TDerived Derived(Base); // <=== ERROR
}
so, I have base and derived classes, and want to use "using TBase::TBase" to pull copy ctor from base class to be able to create instance of derived class in such way:
TDerived Derived(Base);
But all compilers rejects this with these error messages
7 : note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'TBase' to 'const TDerived' for 1st argument
Why? What am I doing wrong? Why "using TBase::TBase" does not work in this situation?
UPDATE
How can be explained following piece of code from cppreference.com?
struct B1 {
B1(int);
};
struct D1 : B1 {
using B1::B1;
// The set of inherited constructors is
// 1. B1(const B1&)
// 2. B1(B1&&)
// 3. B1(int)
Copy and move consturctors (and the default constructor) are never inherited, simply because the standard says so. All other constructors are.
That comment on cppreference was misleading(1). The same comment in the standard says:
The candidate set of inherited constructors in D1 for B1 is
(Emphasis mine).
The standard then goes on to say that only the D1(int) constructor is actually inherited. The copy and move constructors for D1 are implicitly-declared as for any other class, not inherited.
Refer to C++14 12.9 [class.inhctor] for details.
(1) I submitted a change to cppreference to hopefully clarify this.
If you further read the same piece of code, it says:
// D1 has the following constructors:
// 1. D1()
// 2. D1(const D1&)
// 3. D1(D1&&)
// 4. D1(int) <- inherited
};
Thus a copy ctor is still the copy ctor, it accepts an argument of class TDerived. D1(int) is generated automatically nevertheless.
As per standard 12.6.3/p1 Initialization by inherited constructor [class.inhctor.init] (Emphasis Mine):
When a constructor for type B is invoked to initialize an object of a
different type D (that is, when the constructor was inherited
(7.3.3)), initialization proceeds as if a defaulted default
constructor were used to initialize the D object and each base class
subobject from which the constructor was inherited, except that the B
subobject is initialized by the invocation of the inherited
constructor. The complete initialization is considered to be a single
function call; in particular, the initialization of the inherited
constructor’s parameters is sequenced before the initialization of any
part of the D object.
Thus, constructors are not actually inherited but rather they're implicitly or explicitly called by the respective derived constructor. Also keep in mind that the inherited constructors are simply calling the base constructors and do not perform any member initialization in the derived object.
To clarify this consider the following example:
struct Base {
Base(int);
...
};
struct Derived : Base {
using Base::Base;
...
};
The above Derived class definition is syntactically equivalent with:
struct Derived : Base {
Derived(int i) : Base(i) {}
...
};
That is, the using declaration in the Derived class implicitly defines the constructor Derived(int). At this point mind also that if the constructor is inherited from multiple base class sub-objects Derived, the program is ill-formed.
In the same manner you've been lead to the logical conclusion that since I've declared in the base class a copy constructor with the using declaration:
class TBase {
public:
TBase();
TBase(const TBase &);
};
class TDerived: public TBase {
public:
using TBase::TBase;
};
I would get the following syntactical equivalent Derived class:
class TDerived: public TBase {
public:
TDerived() : Base() {}
TDerived(TBase const &other) : Base(other) {}
};
However, this is not the case. You can't "inherit" a copy constructor neither a default constructor neither a move constructor. Why? because this is how the C++ standard dictates so.
What you can do instead is to define a user defined constructor that will take as input a base class object:
class TDerived: public TBase {
public:
TDerived(TBase const &other) {}
};
After all TDerived and TBase are different classes even though the first inherits the second one.
such is the code,and with the error:"illegal member initialization: 'a' is not a base or member",what is the meaning of the error info,and why??
class A{
public:
int a;
};
class B:public A{
public:
B();
};
B::B():a(10){ // put "a(10)" into the constructor body is right
}
By the time the constructor of the derived class is invoked, the base class must already be constructed. So it's already too late. To see why it must be this way, consider:
class Base
{
public:
int i;
Base(int q) : i(q) { ; }
};
class Middle : public Base
{
public:
Middle() : Base(2) { printf("i=%d\n", i); }
};
class Derived : public Middle
{
public:
Derived() : i(3) { ; }
}
Now, think about it. The Middle constructor has to run before the Derived constructor. And the Middle constructor ensures that i is 2. So how can the Derived constructor later re-construct it with a different value?
See this answer for a more complete explanation of what's going on here. Basically, you cannot initialise A::a in the initialiser of B, because it is ill-formed according to the standard.
One more important piece of information - remember that if a class does not initialise a member object via the constructor initialization list, then the default constructor for that member will be invoked before that first statement in the base class constructor is executed. When you use the initialiser, what you are actually doing is specifying a constructor to be used INSTEAD of the default constructor.
Clearly when you are constructing a derived class, the parent class member has thus already been constructed, so you cannot reconstruct the base member again, which is what you are trying to do in this example by including it in the derived class initialisation list.
As an aside, if you all you want is to have the derived class be able to set the value of A::a to a specific value, then use the following code instead:
B::B()
{
a = 10;
}
Because the base class may want to initialize things its own way. The proper method is to call on of the base constructors in the initializer list and let the base ctor initialize itself.