Reputation: 2014
Under MSVC 19.16, if class B explicitly inherits from class A the constructors, and also defines its own constructors, the inherited constructors are ignored.
class A {
public:
A() {}
A(int x) {}
};
class B : public A {
public:
using A::A;
B(double x) : A() {}
};
int main()
{
B b; // error C2512: 'B': no appropriate default constructor available
// note: see declaration of 'B'
return 0;
}
Compiles correctly under gcc. Anyone knows if it is a compiler bug, or something I miss? Thanks.
Upvotes: 4
Views: 320
Reputation: 14714
Arguably not a bug.
C++14 [class.inhctor] ¶3
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...
So before C++17, default constructors were not inheritable, and your example was ill-formed.
That changed when P0136 removed the whole [class.inhctor] section and put the wording for inheriting constructors in [namespace.udecl] instead. This was voted in to C++17, but as it was part of a defect resolution, implementations are also permitted to apply it retroactively to previous revisions of the standard.
So your example is valid C++17, and potentially valid C++11 and C++14 depending on whether your compiler vendor has chosen to apply this change retroactively when compiling according to those revisions of the standard. Conforming compilers must accept this example when compiling in C++17 mode, and are within their rights to accept or reject this example when compiling in C++11 or C++14 mode, depending on their vendors' decisions.
CWG2273 may also be related.
Note that in your example MSVC 19.16 is only ignoring A::A()
. It is not ignoring A::A(int x)
.
Upvotes: 5
Reputation: 66200
I suppose it's a MSVC 19.16 compiler bug.
If you write
class B : public A {
B(double) : A() {}
};
// ...
B b;
you should get an error from every compiler because the B(double)
constructor delete the default B()
constructor.
But, according this page (look for "Inheriting constructors")
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 an inherited constructor, it is accessible if it would be accessible when used to construct an object of the corresponding base class: the accessibility of the using-declaration that introduced it is ignored.
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.
So, in this case, the using A::A;
declaration should transform the A
constructors in B
constructors.
Another example.
Defining B
with only a constructor that receive a std::string
,
class B : public A {
public:
using A::A;
B(std::string) : A() {}
};
should be possible initialize B
with an integer
B b(1);
because the A(int)
constructor is inherited as a B
constructor.
Upvotes: 0
Reputation: 389
If we specify /std:c++14
, it still compiles with an error. But /std:c++17
or /std:c++latest
make it compile.
So it seems like a bug in MSVC.
Upvotes: 0