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?