struct B {
void f();
private:
B(int, int = 0);
};
struct D : B { using B::B; };
void B::f() {
auto a = D{0};
auto b = D(0);
auto c = D(0, 0);
D x{0};
D y(0);
D z(0, 0);
}
GCC accepts (since 7.1; previously rejects all).
Clang accepts b
and xyz
but rejects a
and c
.
MSVC rejects all in C++14 mode but accepts all in C++17 mode.
Which compiler(s) are correct? Did the rules change between C++14 and C++17 - if so, why in C++14 is B::f
not allowed to access its own constructors (named via D
)? And why does Clang only accept the (function-style) cast at b
and not list-initialization at a
or constructor call at c
?
(Making B
a friend
of D
makes Clang accept, except for older versions (3.8 and older), and MSVC in C++14 mode. It doesn't make a difference to gcc.)
Now, I know that C++14 says:
A constructor so declared [as an inheriting constructor] has the same access as the corresponding constructor in [the base class] X.
And in C++17 the rules were simplified, clarified and moved to [namespace.udecl]:
A using-declarator that names a constructor does not create a synonym; instead, the additional constructors are accessible if they would be accessible when used to construct an object of the corresponding base class, and the accessibility of the using-declaration is ignored.
So, I thought that perhaps "has the same access as" means the inherited constructors are private
to D
so only accessible to D
(and its friends), rather than private
to B
(and so accessible to B::f
) as are their corresponding constructors in B
. But under that interpretation, one would be able to subvert access checking and access private constructors of one's base class by inheriting them. So surely in C++14 the intent of the wording is the same as expressed more clearly in C++17 - but then why does MSVC change its behavior depending on language version?